mirror of
https://github.com/k8sgpt-ai/k8sgpt.git
synced 2026-03-18 19:17:25 +00:00
Compare commits
35 Commits
v0.4.23
...
renovate/d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b80d8354af | ||
|
|
2276b12b0f | ||
|
|
fd5bba6ab3 | ||
|
|
19a172e575 | ||
|
|
36de157e21 | ||
|
|
458aa9deba | ||
|
|
285c1353d5 | ||
|
|
4f63e9737c | ||
|
|
a56e4788c3 | ||
|
|
99911fbb3a | ||
|
|
abc46474e3 | ||
|
|
1a8f1d47a4 | ||
|
|
1f2ff98834 | ||
|
|
c80b2e2c34 | ||
|
|
867bce1907 | ||
|
|
f5fb2a7e12 | ||
|
|
a303ffa21c | ||
|
|
21369c5c09 | ||
|
|
40ffcbec6b | ||
|
|
7fe3bdbd95 | ||
|
|
e7b7a5db47 | ||
|
|
5480051230 | ||
|
|
ee6f58443b | ||
|
|
f1d2e306f3 | ||
|
|
83c5d67084 | ||
|
|
291e42dc4b | ||
|
|
8bbffed643 | ||
|
|
53345895de | ||
|
|
7e332761d8 | ||
|
|
0239b2fe6e | ||
|
|
c5c9135900 | ||
|
|
5e86f4925c | ||
|
|
0cf4cae07e | ||
|
|
e385e77da9 | ||
|
|
c47ae595fb |
10
.github/workflows/build_container.yaml
vendored
10
.github/workflows/build_container.yaml
vendored
@@ -14,7 +14,7 @@ on:
|
||||
- "**.md"
|
||||
|
||||
env:
|
||||
GO_VERSION: "~1.23"
|
||||
GO_VERSION: "~1.24"
|
||||
IMAGE_NAME: "k8sgpt"
|
||||
REGISTRY_IMAGE: ghcr.io/k8sgpt-ai/k8sgpt
|
||||
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
@@ -83,7 +83,7 @@ jobs:
|
||||
type=raw,value=dev-${{ env.DATETIME }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
|
||||
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
|
||||
|
||||
- name: Build and push multi-arch image
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
|
||||
|
||||
2
.github/workflows/golangci_lint.yaml
vendored
2
.github/workflows/golangci_lint.yaml
vendored
@@ -9,7 +9,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8
|
||||
|
||||
16
.github/workflows/release.yaml
vendored
16
.github/workflows/release.yaml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
# Release-please creates a PR that tracks all changes
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
|
||||
|
||||
- uses: google-github-actions/release-please-action@e4dc86ba9405554aeba3c6bb2d169500e7d3b4ee # v4.1.1
|
||||
id: release
|
||||
@@ -55,17 +55,17 @@ jobs:
|
||||
docker-images: true
|
||||
swap-storage: true
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
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'
|
||||
go-version: '~1.24'
|
||||
- name: Download Syft
|
||||
uses: anchore/sbom-action/download-syft@55dc4ee22412511ee8c3142cbea40418e6cec693 # v0.17.8
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6
|
||||
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6
|
||||
with:
|
||||
# either 'goreleaser' (default) or 'goreleaser-pro'
|
||||
distribution: goreleaser
|
||||
@@ -91,16 +91,16 @@ jobs:
|
||||
IMAGE_NAME: k8sgpt
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
|
||||
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
|
||||
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3
|
||||
with:
|
||||
registry: "ghcr.io"
|
||||
username: ${{ github.actor }}
|
||||
|
||||
2
.github/workflows/semantic_pr.yaml
vendored
2
.github/workflows/semantic_pr.yaml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
pull-requests: read # Needed for reading prs
|
||||
steps:
|
||||
- name: Validate Pull Request
|
||||
uses: amannn/action-semantic-pull-request@0723387faaf9b38adef4775cd42cfd5155ed6017 # v5.5.3
|
||||
uses: amannn/action-semantic-pull-request@fdd4d3ddf614fbcd8c29e4b106d3bbe0cb2c605d # v6.0.1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
||||
6
.github/workflows/test.yaml
vendored
6
.github/workflows/test.yaml
vendored
@@ -9,16 +9,16 @@ on:
|
||||
- main
|
||||
|
||||
env:
|
||||
GO_VERSION: "~1.22"
|
||||
GO_VERSION: "~1.24"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- 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 }}
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
{".":"0.4.23"}
|
||||
{".":"0.4.30"}
|
||||
90
CHANGELOG.md
90
CHANGELOG.md
@@ -1,5 +1,95 @@
|
||||
# Changelog
|
||||
|
||||
## [0.4.30](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.4.29...v0.4.30) (2026-02-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* validate namespace before running custom analyzers ([#1617](https://github.com/k8sgpt-ai/k8sgpt/issues/1617)) ([458aa9d](https://github.com/k8sgpt-ai/k8sgpt/commit/458aa9debac7590eb0855ffd12141b702e999a36))
|
||||
|
||||
## [0.4.29](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.4.28...v0.4.29) (2026-02-20)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **serve:** add short flag and env var for metrics port ([#1616](https://github.com/k8sgpt-ai/k8sgpt/issues/1616)) ([4f63e97](https://github.com/k8sgpt-ai/k8sgpt/commit/4f63e9737c6a2306686bd3b6f37e81f210665949))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** update k8s.io/utils digest to b8788ab ([#1572](https://github.com/k8sgpt-ai/k8sgpt/issues/1572)) ([a56e478](https://github.com/k8sgpt-ai/k8sgpt/commit/a56e4788c3361a64df17175f163f33422a8fe606))
|
||||
* use proper JSON marshaling for customrest prompt to handle special characters ([#1615](https://github.com/k8sgpt-ai/k8sgpt/issues/1615)) ([99911fb](https://github.com/k8sgpt-ai/k8sgpt/commit/99911fbb3ac8c950fd7ee1b3210f8a9c2a6b0ad7)), closes [#1556](https://github.com/k8sgpt-ai/k8sgpt/issues/1556)
|
||||
|
||||
|
||||
### Refactoring
|
||||
|
||||
* improve MCP server handlers with better error handling and pagination ([#1613](https://github.com/k8sgpt-ai/k8sgpt/issues/1613)) ([abc4647](https://github.com/k8sgpt-ai/k8sgpt/commit/abc46474e372bcd27201f1a64372c04269acee13))
|
||||
|
||||
## [0.4.28](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.4.27...v0.4.28) (2026-02-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add Groq as LLM provider ([#1600](https://github.com/k8sgpt-ai/k8sgpt/issues/1600)) ([867bce1](https://github.com/k8sgpt-ai/k8sgpt/commit/867bce1907f5dd3387128b72c694e98091d55554))
|
||||
* multiple security fixes. Prometheus: v0.302.1 → v0.306.0 ([#1597](https://github.com/k8sgpt-ai/k8sgpt/issues/1597)) ([f5fb2a7](https://github.com/k8sgpt-ai/k8sgpt/commit/f5fb2a7e12e14fad8107940aeead5e60b064add1))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* align CI Go versions with go.mod to ensure consistency ([#1611](https://github.com/k8sgpt-ai/k8sgpt/issues/1611)) ([1f2ff98](https://github.com/k8sgpt-ai/k8sgpt/commit/1f2ff988342b8ef2aa3e3263eb845c0ee09fe24c))
|
||||
* **deps:** update module gopkg.in/yaml.v2 to v3 ([#1550](https://github.com/k8sgpt-ai/k8sgpt/issues/1550)) ([7fe3bdb](https://github.com/k8sgpt-ai/k8sgpt/commit/7fe3bdbd952bc9a1975121de5f21ad31dc1f691d))
|
||||
* use MaxCompletionTokens instead of deprecated MaxTokens for OpenAI ([#1604](https://github.com/k8sgpt-ai/k8sgpt/issues/1604)) ([c80b2e2](https://github.com/k8sgpt-ai/k8sgpt/commit/c80b2e2c346845336593ce515fe90fd501b1d0a7))
|
||||
|
||||
|
||||
### Other
|
||||
|
||||
* **deps:** update actions/checkout digest to 93cb6ef ([#1592](https://github.com/k8sgpt-ai/k8sgpt/issues/1592)) ([40ffcbe](https://github.com/k8sgpt-ai/k8sgpt/commit/40ffcbec6b65e3a99e40be5f414a3f2c087bffbb))
|
||||
* **deps:** update actions/setup-go digest to 40f1582 ([#1593](https://github.com/k8sgpt-ai/k8sgpt/issues/1593)) ([a303ffa](https://github.com/k8sgpt-ai/k8sgpt/commit/a303ffa21c7ede3dd9391185bc91fb3b4e8276b6))
|
||||
* util tests ([#1594](https://github.com/k8sgpt-ai/k8sgpt/issues/1594)) ([21369c5](https://github.com/k8sgpt-ai/k8sgpt/commit/21369c5c0917fd2b6ae4173378b2e257e2b1de7b))
|
||||
|
||||
## [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)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add ClusterServiceVersion, Subscription, InstallPlan, OperatorGroup, and CatalogSource analyzers ([#1564](https://github.com/k8sgpt-ai/k8sgpt/issues/1564)) ([0cf4cae](https://github.com/k8sgpt-ai/k8sgpt/commit/0cf4cae07e32a0025246abcf2d1a5a91f82d093a))
|
||||
* reintroduced inference code ([#1548](https://github.com/k8sgpt-ai/k8sgpt/issues/1548)) ([7e33276](https://github.com/k8sgpt-ai/k8sgpt/commit/7e332761d89d953989b4f33509208dd4db4d4b91))
|
||||
* update helm charts with mcp support and fix Google ADA issue ([#1568](https://github.com/k8sgpt-ai/k8sgpt/issues/1568)) ([5334589](https://github.com/k8sgpt-ai/k8sgpt/commit/53345895deec4c74cac00ee3fd5e230f6a92cf4a))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* migrated to more actively maintained mcp golang lib and added AI explain ([#1557](https://github.com/k8sgpt-ai/k8sgpt/issues/1557)) ([c47ae59](https://github.com/k8sgpt-ai/k8sgpt/commit/c47ae595fb9fc5bf22afef3bc6764b3e87e4553d))
|
||||
|
||||
|
||||
### Other
|
||||
|
||||
* **deps:** update actions/checkout action to v5 ([#1562](https://github.com/k8sgpt-ai/k8sgpt/issues/1562)) ([e385e77](https://github.com/k8sgpt-ai/k8sgpt/commit/e385e77da93a65fe52a152bf1f8f1415552698d5))
|
||||
* **deps:** update amannn/action-semantic-pull-request action to v6 ([#1565](https://github.com/k8sgpt-ai/k8sgpt/issues/1565)) ([c5c9135](https://github.com/k8sgpt-ai/k8sgpt/commit/c5c9135900ec6f95b63dac47df751269e7420e87))
|
||||
* **deps:** update docker/login-action digest to 184bdaa ([#1559](https://github.com/k8sgpt-ai/k8sgpt/issues/1559)) ([0239b2f](https://github.com/k8sgpt-ai/k8sgpt/commit/0239b2fe6e7105bbcf3256c559c30ec7065b25f3))
|
||||
* **deps:** update goreleaser/goreleaser-action digest to e435ccd ([#1569](https://github.com/k8sgpt-ai/k8sgpt/issues/1569)) ([5e86f49](https://github.com/k8sgpt-ai/k8sgpt/commit/5e86f4925c4209b0eb2959227229c2994cfc5b6f))
|
||||
|
||||
## [0.4.23](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.4.22...v0.4.23) (2025-08-08)
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
We're happy that you want to contribute to this project. Please read the sections to make the process as smooth as possible.
|
||||
|
||||
## Requirements
|
||||
- Golang `1.23`
|
||||
- Golang `1.24+`
|
||||
- An OpenAI API key
|
||||
* OpenAI API keys can be obtained from [OpenAI](https://platform.openai.com/account/api-keys)
|
||||
* You can set the API key for k8sgpt using `./k8sgpt auth key`
|
||||
|
||||
482
MCP.md
Normal file
482
MCP.md
Normal 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)
|
||||
6
Makefile
6
Makefile
@@ -85,6 +85,12 @@ docker-build:
|
||||
@echo "===========> Building docker image"
|
||||
docker buildx build --build-arg=VERSION="$$(git describe --tags --abbrev=0)" --build-arg=COMMIT="$$(git rev-parse --short HEAD)" --build-arg DATE="$$(date +%FT%TZ)" --platform="linux/amd64,linux/arm64" -t ${IMG} -f container/Dockerfile . --push
|
||||
|
||||
## docker-build-local: Build docker image for local testing
|
||||
.PHONY: docker-build-local
|
||||
docker-build-local:
|
||||
@echo "===========> Building docker image for local testing"
|
||||
docker build --build-arg=VERSION="$$(git describe --tags --abbrev=0)" --build-arg=COMMIT="$$(git rev-parse --short HEAD)" --build-arg DATE="$$(date +%FT%TZ)" -t k8sgpt:local -f container/Dockerfile .
|
||||
|
||||
## fmt: Run go fmt against code.
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
|
||||
68
README.md
68
README.md
@@ -21,6 +21,10 @@ It has SRE experience codified into its analyzers and helps to pull out the most
|
||||
|
||||
_Out of the box integration with OpenAI, Azure, Cohere, Amazon Bedrock, Google Gemini and local models._
|
||||
|
||||
|
||||
> **Sister project:** Check out [sympozium](https://github.com/AlexsJones/sympozium/) for managing agents in Kubernetes.
|
||||
|
||||
|
||||
<a href="https://www.producthunt.com/posts/k8sgpt?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-k8sgpt" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=389489&theme=light" alt="K8sGPT - K8sGPT gives Kubernetes Superpowers to everyone | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a> <a href="https://hellogithub.com/repository/9dfe44c18dfb4d6fa0181baf8b2cf2e1" target="_blank"><img src="https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=9dfe44c18dfb4d6fa0181baf8b2cf2e1&claim_uid=gqG4wmzkMrP0eFy" alt="Featured|HelloGitHub" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||
|
||||
|
||||
@@ -34,6 +38,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 +67,7 @@ brew install k8sgpt
|
||||
<!---x-release-please-start-version-->
|
||||
|
||||
```
|
||||
sudo rpm -ivh https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.23/k8sgpt_386.rpm
|
||||
sudo rpm -ivh https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.30/k8sgpt_386.rpm
|
||||
```
|
||||
<!---x-release-please-end-->
|
||||
|
||||
@@ -70,7 +75,7 @@ brew install k8sgpt
|
||||
|
||||
<!---x-release-please-start-version-->
|
||||
```
|
||||
sudo rpm -ivh https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.23/k8sgpt_amd64.rpm
|
||||
sudo rpm -ivh https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.30/k8sgpt_amd64.rpm
|
||||
```
|
||||
<!---x-release-please-end-->
|
||||
</details>
|
||||
@@ -83,7 +88,7 @@ brew install k8sgpt
|
||||
<!---x-release-please-start-version-->
|
||||
|
||||
```
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.23/k8sgpt_386.deb
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.30/k8sgpt_386.deb
|
||||
sudo dpkg -i k8sgpt_386.deb
|
||||
```
|
||||
|
||||
@@ -94,7 +99,7 @@ sudo dpkg -i k8sgpt_386.deb
|
||||
<!---x-release-please-start-version-->
|
||||
|
||||
```
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.23/k8sgpt_amd64.deb
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.30/k8sgpt_amd64.deb
|
||||
sudo dpkg -i k8sgpt_amd64.deb
|
||||
```
|
||||
|
||||
@@ -109,7 +114,7 @@ sudo dpkg -i k8sgpt_amd64.deb
|
||||
|
||||
<!---x-release-please-start-version-->
|
||||
```
|
||||
wget https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.23/k8sgpt_386.apk
|
||||
wget https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.30/k8sgpt_386.apk
|
||||
apk add --allow-untrusted k8sgpt_386.apk
|
||||
```
|
||||
<!---x-release-please-end-->
|
||||
@@ -118,7 +123,7 @@ sudo dpkg -i k8sgpt_amd64.deb
|
||||
|
||||
<!---x-release-please-start-version-->
|
||||
```
|
||||
wget https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.23/k8sgpt_amd64.apk
|
||||
wget https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.30/k8sgpt_amd64.apk
|
||||
apk add --allow-untrusted k8sgpt_amd64.apk
|
||||
```
|
||||
<!---x-release-please-end-->
|
||||
@@ -197,7 +202,7 @@ K8sGPT can be integrated with Claude Desktop to provide AI-powered Kubernetes cl
|
||||
- The MCP server will be automatically detected
|
||||
|
||||
3. Configure Claude Desktop with the following JSON:
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
@@ -270,8 +275,14 @@ you will be able to write your own analyzers.
|
||||
- [x] logAnalyzer
|
||||
- [x] storageAnalyzer
|
||||
- [x] securityAnalyzer
|
||||
- [x] CatalogSource
|
||||
- [x] ClusterCatalog
|
||||
- [x] ClusterExtension
|
||||
- [x] ClusterService
|
||||
- [x] ClusterServiceVersion
|
||||
- [x] OperatorGroup
|
||||
- [x] InstallPlan
|
||||
- [x] Subscription
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -393,6 +404,26 @@ _Serve mode_
|
||||
k8sgpt serve
|
||||
```
|
||||
|
||||
_Serve mode with MCP (Model Context Protocol)_
|
||||
|
||||
```
|
||||
# Enable MCP server on default port 8089
|
||||
k8sgpt serve --mcp --mcp-http
|
||||
|
||||
# Enable MCP server on custom port
|
||||
k8sgpt serve --mcp --mcp-http --mcp-port 8089
|
||||
|
||||
# Full serve mode with MCP
|
||||
k8sgpt serve --mcp --mcp-http --port 8080 --metrics-port 8081 --mcp-port 8089
|
||||
```
|
||||
|
||||
The MCP server enables integration with tools like Claude Desktop and other MCP-compatible clients. It runs on port 8089 by default and provides:
|
||||
- Kubernetes cluster analysis via MCP protocol
|
||||
- Resource information and health status
|
||||
- AI-powered issue explanations and recommendations
|
||||
|
||||
For Helm chart deployment with MCP support, see the `charts/k8sgpt/values-mcp-example.yaml` file.
|
||||
|
||||
_Analysis with serve mode_
|
||||
|
||||
```
|
||||
@@ -673,7 +704,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)
|
||||
|
||||
@@ -49,6 +49,9 @@ K8sGPT supports a variety of AI/LLM providers (backends). Some providers have a
|
||||
- us.amazon.nova-lite-v1:0
|
||||
- anthropic.claude-3-haiku-20240307-v1:0
|
||||
|
||||
> **Note:**
|
||||
> If you use an AWS Bedrock inference profile ARN (e.g., `arn:aws:bedrock:us-east-1:<account>:application-inference-profile/<id>`) as the model, you must still provide a valid modelId (e.g., `anthropic.claude-3-sonnet-20240229-v1:0`). K8sGPT will automatically set the required `X-Amzn-Bedrock-Inference-Profile-ARN` header for you when making requests to Bedrock.
|
||||
|
||||
### Amazon SageMaker
|
||||
- **Model:** User-configurable (any model deployed in your SageMaker endpoint)
|
||||
|
||||
@@ -72,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.
|
||||
@@ -1,5 +1,5 @@
|
||||
apiVersion: v2
|
||||
appVersion: v0.3.0 #x-release-please-version
|
||||
appVersion: v0.4.23 #x-release-please-version
|
||||
description: A Helm chart for K8SGPT
|
||||
name: k8sgpt
|
||||
type: application
|
||||
|
||||
@@ -32,7 +32,13 @@ spec:
|
||||
image: {{ .Values.deployment.image.repository }}:{{ .Values.deployment.image.tag | default .Chart.AppVersion }}
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
args: ["serve"]
|
||||
{{- if .Values.deployment.mcp.enabled }}
|
||||
- containerPort: {{ .Values.deployment.mcp.port | int }}
|
||||
{{- end }}
|
||||
args: ["serve"
|
||||
{{- if .Values.deployment.mcp.enabled }}, "--mcp", "-v","--mcp-http", "--mcp-port", {{ .Values.deployment.mcp.port | quote }}
|
||||
{{- end }}
|
||||
]
|
||||
{{- if .Values.deployment.resources }}
|
||||
resources:
|
||||
{{- toYaml .Values.deployment.resources | nindent 10 }}
|
||||
|
||||
@@ -19,4 +19,9 @@ spec:
|
||||
- name: metrics
|
||||
port: 8081
|
||||
targetPort: 8081
|
||||
{{- if .Values.deployment.mcp.enabled }}
|
||||
- name: mcp
|
||||
port: {{ .Values.deployment.mcp.port | int }}
|
||||
targetPort: {{ .Values.deployment.mcp.port | int }}
|
||||
{{- end }}
|
||||
type: {{ .Values.service.type }}
|
||||
|
||||
39
charts/k8sgpt/values-mcp-example.yaml
Normal file
39
charts/k8sgpt/values-mcp-example.yaml
Normal file
@@ -0,0 +1,39 @@
|
||||
# Example values file to enable MCP (Model Context Protocol) service
|
||||
# Copy this file and modify as needed, then use: helm install -f values-mcp-example.yaml
|
||||
|
||||
deployment:
|
||||
# Enable MCP server
|
||||
mcp:
|
||||
enabled: true
|
||||
port: "8089" # Port for MCP server (default: 8089)
|
||||
http: true # Enable HTTP mode for MCP server
|
||||
|
||||
# Other deployment settings remain the same
|
||||
image:
|
||||
repository: ghcr.io/k8sgpt-ai/k8sgpt
|
||||
tag: "" # defaults to Chart.appVersion if unspecified
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
model: "gpt-3.5-turbo"
|
||||
backend: "openai"
|
||||
resources:
|
||||
limits:
|
||||
cpu: "1"
|
||||
memory: "512Mi"
|
||||
requests:
|
||||
cpu: "0.2"
|
||||
memory: "156Mi"
|
||||
|
||||
# Service configuration
|
||||
service:
|
||||
type: ClusterIP
|
||||
annotations: {}
|
||||
|
||||
# Secret configuration for AI backend
|
||||
secret:
|
||||
secretKey: "" # base64 encoded OpenAI token
|
||||
|
||||
# ServiceMonitor for Prometheus metrics
|
||||
serviceMonitor:
|
||||
enabled: false
|
||||
additionalLabels: {}
|
||||
@@ -7,6 +7,11 @@ deployment:
|
||||
env:
|
||||
model: "gpt-3.5-turbo"
|
||||
backend: "openai" # one of: [ openai | llama ]
|
||||
# MCP (Model Context Protocol) server configuration
|
||||
mcp:
|
||||
enabled: false # Enable MCP server
|
||||
port: "8089" # Port for MCP server
|
||||
http: true # Enable HTTP mode for MCP server
|
||||
resources:
|
||||
limits:
|
||||
cpu: "1"
|
||||
|
||||
@@ -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{
|
||||
@@ -201,6 +203,11 @@ var ServeCmd = &cobra.Command{
|
||||
}()
|
||||
}
|
||||
|
||||
// Allow metrics port to be overridden by environment variable
|
||||
if envMetricsPort := os.Getenv("K8SGPT_METRICS_PORT"); envMetricsPort != "" && !cmd.Flags().Changed("metrics-port") {
|
||||
metricsPort = envMetricsPort
|
||||
}
|
||||
|
||||
server := k8sgptserver.Config{
|
||||
Backend: aiProvider.Name,
|
||||
Port: port,
|
||||
@@ -208,6 +215,7 @@ var ServeCmd = &cobra.Command{
|
||||
EnableHttp: enableHttp,
|
||||
Token: aiProvider.Password,
|
||||
Logger: logger,
|
||||
Filters: filters,
|
||||
}
|
||||
go func() {
|
||||
if err := server.ServeMetrics(); err != nil {
|
||||
@@ -231,10 +239,12 @@ var ServeCmd = &cobra.Command{
|
||||
func init() {
|
||||
// add flag for backend
|
||||
ServeCmd.Flags().StringVarP(&port, "port", "p", "8080", "Port to run the server on")
|
||||
ServeCmd.Flags().StringVarP(&metricsPort, "metrics-port", "", "8081", "Port to run the metrics-server on")
|
||||
ServeCmd.Flags().StringVarP(&metricsPort, "metrics-port", "m", "8081", "Port to run the metrics-server on (env: K8SGPT_METRICS_PORT)")
|
||||
ServeCmd.Flags().StringVarP(&backend, "backend", "b", "openai", "Backend AI provider")
|
||||
ServeCmd.Flags().BoolVarP(&enableHttp, "http", "", false, "Enable REST/http using gppc-gateway")
|
||||
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)")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
166
go.mod
166
go.mod
@@ -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,20 +43,20 @@ 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/metoro-io/mcp-golang v0.11.0
|
||||
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
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
sigs.k8s.io/controller-runtime v0.19.3
|
||||
sigs.k8s.io/gateway-api v1.2.1
|
||||
)
|
||||
@@ -63,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
|
||||
@@ -93,90 +95,70 @@ 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/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // 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/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/gin-gonic/gin v1.10.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.22.1 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // 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
|
||||
github.com/invopop/jsonschema v0.12.0 // indirect
|
||||
github.com/invopop/jsonschema v0.13.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/jpillora/backoff v1.0.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lithammer/fuzzysearch v1.1.8 // indirect
|
||||
github.com/moby/sys/mountinfo v0.7.1 // indirect
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
|
||||
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/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // 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
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 // 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
|
||||
golang.org/x/arch v0.8.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
|
||||
)
|
||||
|
||||
@@ -193,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
|
||||
@@ -218,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
|
||||
@@ -233,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
|
||||
@@ -260,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
|
||||
@@ -271,27 +253,27 @@ require (
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.7.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
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
|
||||
@@ -300,7 +282,7 @@ require (
|
||||
k8s.io/component-base v0.32.2 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
|
||||
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2
|
||||
oras.land/oras-go v1.2.5 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.18.0 // indirect
|
||||
|
||||
399
go.sum
399
go.sum
@@ -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=
|
||||
@@ -789,13 +789,8 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembj
|
||||
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
|
||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o=
|
||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
|
||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
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=
|
||||
@@ -810,10 +805,6 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
@@ -826,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=
|
||||
@@ -836,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=
|
||||
@@ -858,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=
|
||||
@@ -896,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=
|
||||
@@ -909,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=
|
||||
@@ -926,13 +921,7 @@ github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/
|
||||
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
|
||||
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g=
|
||||
@@ -945,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=
|
||||
@@ -965,16 +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-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
|
||||
github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
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=
|
||||
@@ -985,8 +968,6 @@ github.com/go-zookeeper/zk v1.0.4/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
|
||||
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
|
||||
@@ -1062,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=
|
||||
@@ -1092,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=
|
||||
@@ -1108,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=
|
||||
@@ -1121,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=
|
||||
@@ -1130,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=
|
||||
@@ -1148,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=
|
||||
@@ -1179,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=
|
||||
@@ -1192,10 +1173,10 @@ github.com/imdario/mergo v1.0.1 h1:lFIgOs30GMaV/2+qQ+eEBLbUL6h1YosdohE3ODy4hTs=
|
||||
github.com/imdario/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI=
|
||||
github.com/invopop/jsonschema v0.12.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/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.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=
|
||||
@@ -1219,20 +1200,19 @@ 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=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00=
|
||||
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
@@ -1255,14 +1235,12 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
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=
|
||||
@@ -1271,6 +1249,8 @@ github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a
|
||||
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mark3labs/mcp-go v0.36.0 h1:rIZaijrRYPeSbJG8/qNDe0hWlGrCJ7FWHNMz2SQpTis=
|
||||
github.com/mark3labs/mcp-go v0.36.0/go.mod h1:T7tUa2jO6MavG+3P25Oy/jR7iCeJPHImCZHRymCn39g=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
@@ -1282,13 +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/metoro-io/mcp-golang v0.11.0 h1:1k+VSE9QaeMTLn0gJ3FgE/DcjsCBsLFnz5eSFbgXUiI=
|
||||
github.com/metoro-io/mcp-golang v0.11.0/go.mod h1:ifLP9ZzKpN1UqFWNTpAHOqSvNkMK6b7d1FSZ5Lu0lN0=
|
||||
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=
|
||||
@@ -1335,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=
|
||||
@@ -1347,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=
|
||||
@@ -1378,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=
|
||||
@@ -1408,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=
|
||||
@@ -1434,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=
|
||||
@@ -1460,14 +1439,18 @@ github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z
|
||||
github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
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=
|
||||
@@ -1490,20 +1473,6 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs=
|
||||
github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||
@@ -1522,6 +1491,8 @@ github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@@ -1536,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=
|
||||
@@ -1548,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=
|
||||
@@ -1576,9 +1549,6 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@@ -1589,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=
|
||||
@@ -1606,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=
|
||||
@@ -1649,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=
|
||||
@@ -1709,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=
|
||||
@@ -1739,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=
|
||||
@@ -1757,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=
|
||||
@@ -1843,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=
|
||||
@@ -1853,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=
|
||||
@@ -1871,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=
|
||||
@@ -1944,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=
|
||||
@@ -2021,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=
|
||||
@@ -2163,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=
|
||||
@@ -2212,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=
|
||||
@@ -2232,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=
|
||||
@@ -2270,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=
|
||||
@@ -2290,8 +2260,8 @@ k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJ
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
|
||||
k8s.io/kubectl v0.32.2 h1:TAkag6+XfSBgkqK9I7ZvwtF0WVtUAvK8ZqTt+5zi1Us=
|
||||
k8s.io/kubectl v0.32.2/go.mod h1:+h/NQFSPxiDZYX/WZaWw9fwYezGLISP0ud8nQKg+3g8=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU=
|
||||
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
|
||||
knative.dev/pkg v0.0.0-20241026180704-25f6002b00f3 h1:uUSDGlOIkdPT4svjlhi+JEnP2Ufw7AM/F5QDYiEL02U=
|
||||
knative.dev/pkg v0.0.0-20241026180704-25f6002b00f3/go.mod h1:FeMbTLlxQqSASwlRCrYEOsZ0OKUgSj52qxhECwYCJsw=
|
||||
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
@@ -2328,7 +2298,6 @@ modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw
|
||||
modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw=
|
||||
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo=
|
||||
oras.land/oras-go v1.2.5/go.mod h1:PuAwRShRZCsZb7g8Ar3jKKQR/2A/qN+pkYxIOd/FAoo=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
|
||||
@@ -14,6 +14,8 @@ import (
|
||||
awsconfig "github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/service/bedrock"
|
||||
"github.com/aws/aws-sdk-go-v2/service/bedrockruntime"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
smithyhttp "github.com/aws/smithy-go/transport/http"
|
||||
)
|
||||
|
||||
const amazonbedrockAIClientName = "amazonbedrock"
|
||||
@@ -456,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)
|
||||
@@ -560,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 {
|
||||
@@ -583,8 +585,30 @@ func (a *AmazonBedRockClient) GetCompletion(ctx context.Context, prompt string)
|
||||
Accept: aws.String("application/json"),
|
||||
}
|
||||
|
||||
// Detect if the model name is an inference profile ARN and set the header if so
|
||||
var optFns []func(*bedrockruntime.Options)
|
||||
if validateInferenceProfileArn(a.model.Config.ModelName) {
|
||||
inferenceProfileArn := a.model.Config.ModelName
|
||||
optFns = append(optFns, func(options *bedrockruntime.Options) {
|
||||
options.APIOptions = append(options.APIOptions, func(stack *middleware.Stack) error {
|
||||
return stack.Initialize.Add(middleware.InitializeMiddlewareFunc("InferenceProfileHeader", func(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) (out middleware.InitializeOutput, metadata middleware.Metadata, err error) {
|
||||
req, ok := in.Parameters.(*smithyhttp.Request)
|
||||
if ok {
|
||||
req.Header.Set("X-Amzn-Bedrock-Inference-Profile-ARN", inferenceProfileArn)
|
||||
}
|
||||
return next.HandleInitialize(ctx, in)
|
||||
}), middleware.Before)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Invoke the model
|
||||
resp, err := a.client.InvokeModel(ctx, params)
|
||||
var resp *bedrockruntime.InvokeModelOutput
|
||||
if len(optFns) > 0 {
|
||||
resp, err = a.client.InvokeModel(ctx, params, optFns...)
|
||||
} else {
|
||||
resp, err = a.client.InvokeModel(ctx, params)
|
||||
}
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "InvalidAccessKeyId") || strings.Contains(err.Error(), "SignatureDoesNotMatch") || strings.Contains(err.Error(), "NoCredentialProviders") {
|
||||
return "", fmt.Errorf("AWS credentials are invalid or missing. Please check your AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables or AWS config. Details: %v", err)
|
||||
|
||||
102
pkg/ai/groq.go
Normal file
102
pkg/ai/groq.go
Normal 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
|
||||
}
|
||||
@@ -34,6 +34,7 @@ var (
|
||||
&OCIGenAIClient{},
|
||||
&CustomRestClient{},
|
||||
&IBMWatsonxAIClient{},
|
||||
&GroqClient{},
|
||||
}
|
||||
Backends = []string{
|
||||
openAIClientName,
|
||||
@@ -50,6 +51,7 @@ var (
|
||||
ociClientName,
|
||||
CustomRestClientName,
|
||||
ibmWatsonxAIClientName,
|
||||
groqAIClientName,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -16,6 +16,7 @@ package analysis
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
@@ -34,6 +35,7 @@ import (
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||
"github.com/schollz/progressbar/v3"
|
||||
"github.com/spf13/viper"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type Analysis struct {
|
||||
@@ -226,6 +228,15 @@ func (a *Analysis) CustomAnalyzersAreAvailable() bool {
|
||||
}
|
||||
|
||||
func (a *Analysis) RunCustomAnalysis() {
|
||||
// Validate namespace if specified, consistent with built-in filter behavior
|
||||
if a.Namespace != "" && a.Client != nil {
|
||||
_, err := a.Client.Client.CoreV1().Namespaces().Get(a.Context, a.Namespace, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
a.Errors = append(a.Errors, fmt.Sprintf("namespace %q not found: %s", a.Namespace, err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var customAnalyzers []custom.CustomAnalyzer
|
||||
if err := viper.UnmarshalKey("custom_analyzers", &customAnalyzers); err != nil {
|
||||
a.Errors = append(a.Errors, err.Error())
|
||||
@@ -526,7 +537,22 @@ func (a *Analysis) getAIResultForSanitizedFailures(texts []string, promptTmpl st
|
||||
// Process template.
|
||||
prompt := fmt.Sprintf(strings.TrimSpace(promptTmpl), a.Language, inputKey)
|
||||
if a.AIClient.GetName() == ai.CustomRestClientName {
|
||||
prompt = fmt.Sprintf(ai.PromptMap["raw"], a.Language, inputKey, prompt)
|
||||
// Use proper JSON marshaling to handle special characters in error messages
|
||||
// This fixes issues with quotes, newlines, and other special chars in inputKey
|
||||
customRestPrompt := struct {
|
||||
Language string `json:"language"`
|
||||
Message string `json:"message"`
|
||||
Prompt string `json:"prompt"`
|
||||
}{
|
||||
Language: a.Language,
|
||||
Message: inputKey,
|
||||
Prompt: prompt,
|
||||
}
|
||||
promptBytes, err := json.Marshal(customRestPrompt)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to marshal customrest prompt: %w", err)
|
||||
}
|
||||
prompt = string(promptBytes)
|
||||
}
|
||||
response, err := a.AIClient.GetCompletion(a.Context, prompt)
|
||||
if err != nil {
|
||||
|
||||
@@ -59,6 +59,11 @@ var additionalAnalyzerMap = map[string]common.IAnalyzer{
|
||||
"Security": SecurityAnalyzer{},
|
||||
"ClusterCatalog": ClusterCatalogAnalyzer{},
|
||||
"ClusterExtension": ClusterExtensionAnalyzer{},
|
||||
"ClusterServiceVersion": ClusterServiceVersionAnalyzer{},
|
||||
"Subscription": SubscriptionAnalyzer{},
|
||||
"InstallPlan": InstallPlanAnalyzer{},
|
||||
"CatalogSource": CatalogSourceAnalyzer{},
|
||||
"OperatorGroup": OperatorGroupAnalyzer{},
|
||||
}
|
||||
|
||||
func ListFilters() ([]string, []string, []string) {
|
||||
|
||||
53
pkg/analyzer/catalogsource.go
Normal file
53
pkg/analyzer/catalogsource.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
type CatalogSourceAnalyzer struct{}
|
||||
|
||||
var catSrcGVR = schema.GroupVersionResource{
|
||||
Group: "operators.coreos.com",
|
||||
Version: "v1alpha1",
|
||||
Resource: "catalogsources",
|
||||
}
|
||||
|
||||
func (CatalogSourceAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
|
||||
kind := "CatalogSource"
|
||||
if a.Client.GetDynamicClient() == nil {
|
||||
return nil, fmt.Errorf("dynamic client is nil in %s analyzer", kind)
|
||||
}
|
||||
|
||||
list, err := a.Client.GetDynamicClient().
|
||||
Resource(catSrcGVR).Namespace(metav1.NamespaceAll).
|
||||
List(a.Context, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var results []common.Result
|
||||
for _, item := range list.Items {
|
||||
ns, name := item.GetNamespace(), item.GetName()
|
||||
|
||||
state, _, _ := unstructured.NestedString(item.Object, "status", "connectionState", "lastObservedState")
|
||||
addr, _, _ := unstructured.NestedString(item.Object, "status", "connectionState", "address")
|
||||
|
||||
// Only report if state is present and not READY
|
||||
if state != "" && strings.ToUpper(state) != "READY" {
|
||||
results = append(results, common.Result{
|
||||
Kind: kind,
|
||||
Name: ns + "/" + name,
|
||||
Error: []common.Failure{{
|
||||
Text: fmt.Sprintf("connectionState=%s (address=%s)", state, addr),
|
||||
}},
|
||||
})
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
107
pkg/analyzer/catalogsource_test.go
Normal file
107
pkg/analyzer/catalogsource_test.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
dynamicfake "k8s.io/client-go/dynamic/fake"
|
||||
)
|
||||
|
||||
func TestCatalogSourceAnalyzer_UnhealthyState_ReturnsResult(t *testing.T) {
|
||||
cs := &unstructured.Unstructured{
|
||||
Object: map[string]any{
|
||||
"apiVersion": "operators.coreos.com/v1alpha1",
|
||||
"kind": "CatalogSource",
|
||||
"metadata": map[string]any{
|
||||
"name": "broken-operators-external",
|
||||
"namespace": "openshift-marketplace",
|
||||
},
|
||||
"status": map[string]any{
|
||||
"connectionState": map[string]any{
|
||||
"lastObservedState": "TRANSIENT_FAILURE",
|
||||
"address": "not-a-real-host.invalid:50051",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
listKinds := map[schema.GroupVersionResource]string{
|
||||
{Group: "operators.coreos.com", Version: "v1alpha1", Resource: "catalogsources"}: "CatalogSourceList",
|
||||
}
|
||||
scheme := runtime.NewScheme()
|
||||
dc := dynamicfake.NewSimpleDynamicClientWithCustomListKinds(scheme, listKinds, cs)
|
||||
|
||||
a := common.Analyzer{
|
||||
Context: context.TODO(),
|
||||
Client: &kubernetes.Client{DynamicClient: dc},
|
||||
}
|
||||
|
||||
res, err := (CatalogSourceAnalyzer{}).Analyze(a)
|
||||
if err != nil {
|
||||
t.Fatalf("Analyze error: %v", err)
|
||||
}
|
||||
if len(res) != 1 {
|
||||
t.Fatalf("expected 1 result, got %d", len(res))
|
||||
}
|
||||
if res[0].Kind != "CatalogSource" || !strings.Contains(res[0].Name, "openshift-marketplace/broken-operators-external") {
|
||||
t.Fatalf("unexpected result: %#v", res[0])
|
||||
}
|
||||
if len(res[0].Error) == 0 || !strings.Contains(res[0].Error[0].Text, "TRANSIENT_FAILURE") {
|
||||
t.Fatalf("expected TRANSIENT_FAILURE in message, got %#v", res[0].Error)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCatalogSourceAnalyzer_HealthyOrNoState_Ignored(t *testing.T) {
|
||||
// One READY (healthy), one with no status at all: both should be ignored.
|
||||
ready := &unstructured.Unstructured{
|
||||
Object: map[string]any{
|
||||
"apiVersion": "operators.coreos.com/v1alpha1",
|
||||
"kind": "CatalogSource",
|
||||
"metadata": map[string]any{
|
||||
"name": "ready-operators",
|
||||
"namespace": "openshift-marketplace",
|
||||
},
|
||||
"status": map[string]any{
|
||||
"connectionState": map[string]any{
|
||||
"lastObservedState": "READY",
|
||||
"address": "somewhere",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
nostate := &unstructured.Unstructured{
|
||||
Object: map[string]any{
|
||||
"apiVersion": "operators.coreos.com/v1alpha1",
|
||||
"kind": "CatalogSource",
|
||||
"metadata": map[string]any{
|
||||
"name": "no-status-operators",
|
||||
"namespace": "openshift-marketplace",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
listKinds := map[schema.GroupVersionResource]string{
|
||||
{Group: "operators.coreos.com", Version: "v1alpha1", Resource: "catalogsources"}: "CatalogSourceList",
|
||||
}
|
||||
scheme := runtime.NewScheme()
|
||||
dc := dynamicfake.NewSimpleDynamicClientWithCustomListKinds(scheme, listKinds, ready, nostate)
|
||||
|
||||
a := common.Analyzer{
|
||||
Context: context.TODO(),
|
||||
Client: &kubernetes.Client{DynamicClient: dc},
|
||||
}
|
||||
|
||||
res, err := (CatalogSourceAnalyzer{}).Analyze(a)
|
||||
if err != nil {
|
||||
t.Fatalf("Analyze error: %v", err)
|
||||
}
|
||||
if len(res) != 0 {
|
||||
t.Fatalf("expected 0 results (healthy/nostate ignored), got %d", len(res))
|
||||
}
|
||||
}
|
||||
82
pkg/analyzer/clusterserviceversion.go
Normal file
82
pkg/analyzer/clusterserviceversion.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
type ClusterServiceVersionAnalyzer struct{}
|
||||
|
||||
var csvGVR = schema.GroupVersionResource{
|
||||
Group: "operators.coreos.com", Version: "v1alpha1", Resource: "clusterserviceversions",
|
||||
}
|
||||
|
||||
func (ClusterServiceVersionAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
|
||||
kind := "ClusterServiceVersion"
|
||||
|
||||
if a.Client.GetDynamicClient() == nil {
|
||||
return nil, fmt.Errorf("dynamic client is nil in %s analyzer", kind)
|
||||
}
|
||||
|
||||
list, err := a.Client.GetDynamicClient().
|
||||
Resource(csvGVR).Namespace(metav1.NamespaceAll).
|
||||
List(a.Context, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var results []common.Result
|
||||
for _, item := range list.Items {
|
||||
ns := item.GetNamespace()
|
||||
name := item.GetName()
|
||||
phase, _, _ := unstructured.NestedString(item.Object, "status", "phase")
|
||||
|
||||
var failures []common.Failure
|
||||
if phase != "" && phase != "Succeeded" {
|
||||
// Superfície de condições para contexto
|
||||
if conds, _, _ := unstructured.NestedSlice(item.Object, "status", "conditions"); len(conds) > 0 {
|
||||
if msg := pickWorstCondition(conds); msg != "" {
|
||||
failures = append(failures, common.Failure{Text: fmt.Sprintf("phase=%q: %s", phase, msg)})
|
||||
}
|
||||
} else {
|
||||
failures = append(failures, common.Failure{Text: fmt.Sprintf("phase=%q (see status.conditions)", phase)})
|
||||
}
|
||||
}
|
||||
|
||||
if len(failures) > 0 {
|
||||
results = append(results, common.Result{
|
||||
Kind: kind,
|
||||
Name: ns + "/" + name,
|
||||
Error: failures,
|
||||
})
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// reaproveitamos o heurístico já usado em outros pontos
|
||||
func pickWorstCondition(conds []interface{}) string {
|
||||
for _, c := range conds {
|
||||
m, ok := c.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if s, _ := m["status"].(string); s == "True" {
|
||||
continue
|
||||
}
|
||||
r, _ := m["reason"].(string)
|
||||
msg, _ := m["message"].(string)
|
||||
if r == "" && msg == "" {
|
||||
continue
|
||||
}
|
||||
if r != "" && msg != "" {
|
||||
return r + ": " + msg
|
||||
}
|
||||
return r + msg
|
||||
}
|
||||
return ""
|
||||
}
|
||||
78
pkg/analyzer/clusterserviceversion_test.go
Normal file
78
pkg/analyzer/clusterserviceversion_test.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
dynamicfake "k8s.io/client-go/dynamic/fake"
|
||||
)
|
||||
|
||||
func TestClusterServiceVersionAnalyzer(t *testing.T) {
|
||||
ok := &unstructured.Unstructured{
|
||||
Object: map[string]any{
|
||||
"apiVersion": "operators.coreos.com/v1alpha1",
|
||||
"kind": "ClusterServiceVersion",
|
||||
"metadata": map[string]any{
|
||||
"name": "ok",
|
||||
"namespace": "ns1",
|
||||
},
|
||||
"status": map[string]any{"phase": "Succeeded"},
|
||||
},
|
||||
}
|
||||
|
||||
bad := &unstructured.Unstructured{
|
||||
Object: map[string]any{
|
||||
"apiVersion": "operators.coreos.com/v1alpha1",
|
||||
"kind": "ClusterServiceVersion",
|
||||
"metadata": map[string]any{
|
||||
"name": "bad",
|
||||
"namespace": "ns1",
|
||||
},
|
||||
"status": map[string]any{
|
||||
"phase": "Failed",
|
||||
// IMPORTANT: conditions must be []interface{}, not []map[string]any
|
||||
"conditions": []interface{}{
|
||||
map[string]any{
|
||||
"status": "False",
|
||||
"reason": "ErrorResolving",
|
||||
"message": "missing dep",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
listKinds := map[schema.GroupVersionResource]string{
|
||||
{Group: "operators.coreos.com", Version: "v1alpha1", Resource: "clusterserviceversions"}: "ClusterServiceVersionList",
|
||||
}
|
||||
|
||||
// Use a non-nil scheme with dynamicfake
|
||||
scheme := runtime.NewScheme()
|
||||
dc := dynamicfake.NewSimpleDynamicClientWithCustomListKinds(scheme, listKinds, ok, bad)
|
||||
|
||||
a := common.Analyzer{
|
||||
Context: context.TODO(),
|
||||
Client: &kubernetes.Client{DynamicClient: dc},
|
||||
}
|
||||
|
||||
res, err := (ClusterServiceVersionAnalyzer{}).Analyze(a)
|
||||
if err != nil {
|
||||
t.Fatalf("Analyze error: %v", err)
|
||||
}
|
||||
|
||||
if len(res) != 1 {
|
||||
t.Fatalf("expected 1 result, got %d", len(res))
|
||||
}
|
||||
if res[0].Kind != "ClusterServiceVersion" || !strings.Contains(res[0].Name, "ns1/bad") {
|
||||
t.Fatalf("unexpected result: %#v", res[0])
|
||||
}
|
||||
if len(res[0].Error) == 0 || !strings.Contains(res[0].Error[0].Text, "missing dep") {
|
||||
t.Fatalf("expected 'missing dep' in failure, got %#v", res[0].Error)
|
||||
}
|
||||
}
|
||||
75
pkg/analyzer/installplan_test.go
Normal file
75
pkg/analyzer/installplan_test.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
dynamicfake "k8s.io/client-go/dynamic/fake"
|
||||
)
|
||||
|
||||
func TestInstallPlanAnalyzer(t *testing.T) {
|
||||
ok := &unstructured.Unstructured{
|
||||
Object: map[string]any{
|
||||
"apiVersion": "operators.coreos.com/v1alpha1",
|
||||
"kind": "InstallPlan",
|
||||
"metadata": map[string]any{
|
||||
"name": "ip-ok",
|
||||
"namespace": "ns1",
|
||||
},
|
||||
"status": map[string]any{"phase": "Complete"},
|
||||
},
|
||||
}
|
||||
|
||||
bad := &unstructured.Unstructured{
|
||||
Object: map[string]any{
|
||||
"apiVersion": "operators.coreos.com/v1alpha1",
|
||||
"kind": "InstallPlan",
|
||||
"metadata": map[string]any{
|
||||
"name": "ip-bad",
|
||||
"namespace": "ns1",
|
||||
},
|
||||
"status": map[string]any{
|
||||
"phase": "Failed",
|
||||
"conditions": []interface{}{
|
||||
map[string]any{
|
||||
"reason": "ExecutionError",
|
||||
"message": "something went wrong",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
listKinds := map[schema.GroupVersionResource]string{
|
||||
{Group: "operators.coreos.com", Version: "v1alpha1", Resource: "installplans"}: "InstallPlanList",
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
dc := dynamicfake.NewSimpleDynamicClientWithCustomListKinds(scheme, listKinds, ok, bad)
|
||||
|
||||
a := common.Analyzer{
|
||||
Context: context.TODO(),
|
||||
Client: &kubernetes.Client{DynamicClient: dc},
|
||||
}
|
||||
|
||||
res, err := (InstallPlanAnalyzer{}).Analyze(a)
|
||||
if err != nil {
|
||||
t.Fatalf("Analyze error: %v", err)
|
||||
}
|
||||
|
||||
if len(res) != 1 {
|
||||
t.Fatalf("expected 1 result, got %d", len(res))
|
||||
}
|
||||
if res[0].Kind != "InstallPlan" || !strings.Contains(res[0].Name, "ns1/ip-bad") {
|
||||
t.Fatalf("unexpected result: %#v", res[0])
|
||||
}
|
||||
if len(res[0].Error) == 0 || !strings.Contains(res[0].Error[0].Text, "ExecutionError") {
|
||||
t.Fatalf("expected 'ExecutionError' in failure, got %#v", res[0].Error)
|
||||
}
|
||||
}
|
||||
72
pkg/analyzer/instalplan.go
Normal file
72
pkg/analyzer/instalplan.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
type InstallPlanAnalyzer struct{}
|
||||
|
||||
var ipGVR = schema.GroupVersionResource{
|
||||
Group: "operators.coreos.com", Version: "v1alpha1", Resource: "installplans",
|
||||
}
|
||||
|
||||
func (InstallPlanAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
|
||||
kind := "InstallPlan"
|
||||
if a.Client.GetDynamicClient() == nil {
|
||||
return nil, fmt.Errorf("dynamic client is nil in %s analyzer", kind)
|
||||
}
|
||||
|
||||
list, err := a.Client.GetDynamicClient().
|
||||
Resource(ipGVR).Namespace(metav1.NamespaceAll).
|
||||
List(a.Context, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var results []common.Result
|
||||
for _, item := range list.Items {
|
||||
ns, name := item.GetNamespace(), item.GetName()
|
||||
phase, _, _ := unstructured.NestedString(item.Object, "status", "phase")
|
||||
|
||||
var failures []common.Failure
|
||||
if phase != "" && phase != "Complete" {
|
||||
reason := firstCondStr(&item, "reason")
|
||||
msg := firstCondStr(&item, "message")
|
||||
switch {
|
||||
case reason != "" && msg != "":
|
||||
failures = append(failures, common.Failure{Text: fmt.Sprintf("phase=%q: %s: %s", phase, reason, msg)})
|
||||
case reason != "" || msg != "":
|
||||
failures = append(failures, common.Failure{Text: fmt.Sprintf("phase=%q: %s%s", phase, reason, msg)})
|
||||
default:
|
||||
failures = append(failures, common.Failure{Text: fmt.Sprintf("phase=%q (approval/manual? check status.conditions)", phase)})
|
||||
}
|
||||
}
|
||||
|
||||
if len(failures) > 0 {
|
||||
results = append(results, common.Result{
|
||||
Kind: kind,
|
||||
Name: ns + "/" + name,
|
||||
Error: failures,
|
||||
})
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func firstCondStr(u *unstructured.Unstructured, field string) string {
|
||||
conds, _, _ := unstructured.NestedSlice(u.Object, "status", "conditions")
|
||||
if len(conds) == 0 {
|
||||
return ""
|
||||
}
|
||||
m, _ := conds[0].(map[string]any)
|
||||
if m == nil {
|
||||
return ""
|
||||
}
|
||||
v, _ := m[field].(string)
|
||||
return v
|
||||
}
|
||||
46
pkg/analyzer/operatorgroup.go
Normal file
46
pkg/analyzer/operatorgroup.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
type OperatorGroupAnalyzer struct{}
|
||||
|
||||
var ogGVR = schema.GroupVersionResource{
|
||||
Group: "operators.coreos.com", Version: "v1", Resource: "operatorgroups",
|
||||
}
|
||||
|
||||
func (OperatorGroupAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
|
||||
kind := "OperatorGroup"
|
||||
if a.Client.GetDynamicClient() == nil {
|
||||
return nil, fmt.Errorf("dynamic client is nil in %s analyzer", kind)
|
||||
}
|
||||
|
||||
list, err := a.Client.GetDynamicClient().
|
||||
Resource(ogGVR).Namespace(metav1.NamespaceAll).
|
||||
List(a.Context, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
countByNS := map[string]int{}
|
||||
for _, it := range list.Items {
|
||||
countByNS[it.GetNamespace()]++
|
||||
}
|
||||
|
||||
var results []common.Result
|
||||
for ns, n := range countByNS {
|
||||
if n > 1 {
|
||||
results = append(results, common.Result{
|
||||
Kind: kind,
|
||||
Name: ns,
|
||||
Error: []common.Failure{{Text: fmt.Sprintf("%d OperatorGroups in namespace; this can break CSV resolution", n)}},
|
||||
})
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
70
pkg/analyzer/operatorgroup_test.go
Normal file
70
pkg/analyzer/operatorgroup_test.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
dynamicfake "k8s.io/client-go/dynamic/fake"
|
||||
)
|
||||
|
||||
func TestOperatorGroupAnalyzer(t *testing.T) {
|
||||
og1 := &unstructured.Unstructured{
|
||||
Object: map[string]any{
|
||||
"apiVersion": "operators.coreos.com/v1",
|
||||
"kind": "OperatorGroup",
|
||||
"metadata": map[string]any{
|
||||
"name": "og-1",
|
||||
"namespace": "ns-a",
|
||||
},
|
||||
},
|
||||
}
|
||||
og2 := &unstructured.Unstructured{
|
||||
Object: map[string]any{
|
||||
"apiVersion": "operators.coreos.com/v1",
|
||||
"kind": "OperatorGroup",
|
||||
"metadata": map[string]any{
|
||||
"name": "og-2",
|
||||
"namespace": "ns-a",
|
||||
},
|
||||
},
|
||||
}
|
||||
og3 := &unstructured.Unstructured{
|
||||
Object: map[string]any{
|
||||
"apiVersion": "operators.coreos.com/v1",
|
||||
"kind": "OperatorGroup",
|
||||
"metadata": map[string]any{
|
||||
"name": "og-3",
|
||||
"namespace": "ns-b",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
listKinds := map[schema.GroupVersionResource]string{
|
||||
{Group: "operators.coreos.com", Version: "v1", Resource: "operatorgroups"}: "OperatorGroupList",
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
dc := dynamicfake.NewSimpleDynamicClientWithCustomListKinds(scheme, listKinds, og1, og2, og3)
|
||||
|
||||
a := common.Analyzer{
|
||||
Context: context.TODO(),
|
||||
Client: &kubernetes.Client{DynamicClient: dc},
|
||||
}
|
||||
|
||||
res, err := (OperatorGroupAnalyzer{}).Analyze(a)
|
||||
if err != nil {
|
||||
t.Fatalf("Analyze error: %v", err)
|
||||
}
|
||||
|
||||
if len(res) != 1 {
|
||||
t.Fatalf("expected 1 result for ns-a overlap, got %d", len(res))
|
||||
}
|
||||
if res[0].Kind != "OperatorGroup" || res[0].Name != "ns-a" {
|
||||
t.Fatalf("unexpected result: %#v", res[0])
|
||||
}
|
||||
}
|
||||
55
pkg/analyzer/subscription.go
Normal file
55
pkg/analyzer/subscription.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
type SubscriptionAnalyzer struct{}
|
||||
|
||||
var subGVR = schema.GroupVersionResource{
|
||||
Group: "operators.coreos.com", Version: "v1alpha1", Resource: "subscriptions",
|
||||
}
|
||||
|
||||
func (SubscriptionAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
|
||||
kind := "Subscription"
|
||||
if a.Client.GetDynamicClient() == nil {
|
||||
return nil, fmt.Errorf("dynamic client is nil in %s analyzer", kind)
|
||||
}
|
||||
|
||||
list, err := a.Client.GetDynamicClient().
|
||||
Resource(subGVR).Namespace(metav1.NamespaceAll).
|
||||
List(a.Context, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var results []common.Result
|
||||
for _, item := range list.Items {
|
||||
ns, name := item.GetNamespace(), item.GetName()
|
||||
state, _, _ := unstructured.NestedString(item.Object, "status", "state")
|
||||
conds, _, _ := unstructured.NestedSlice(item.Object, "status", "conditions")
|
||||
|
||||
var failures []common.Failure
|
||||
if state == "" || state == "UpgradePending" || state == "UpgradeAvailable" {
|
||||
msg := "subscription not at latest"
|
||||
if c := pickWorstCondition(conds); c != "" {
|
||||
msg += "; " + c
|
||||
}
|
||||
failures = append(failures, common.Failure{Text: fmt.Sprintf("state=%q: %s", state, msg)})
|
||||
}
|
||||
|
||||
if len(failures) > 0 {
|
||||
results = append(results, common.Result{
|
||||
Kind: kind,
|
||||
Name: ns + "/" + name,
|
||||
Error: failures,
|
||||
})
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
78
pkg/analyzer/subscription_test.go
Normal file
78
pkg/analyzer/subscription_test.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
dynamicfake "k8s.io/client-go/dynamic/fake"
|
||||
)
|
||||
|
||||
func TestSubscriptionAnalyzer(t *testing.T) {
|
||||
ok := &unstructured.Unstructured{
|
||||
Object: map[string]any{
|
||||
"apiVersion": "operators.coreos.com/v1alpha1",
|
||||
"kind": "Subscription",
|
||||
"metadata": map[string]any{
|
||||
"name": "ok-sub",
|
||||
"namespace": "ns1",
|
||||
},
|
||||
"status": map[string]any{
|
||||
"state": "AtLatestKnown",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
bad := &unstructured.Unstructured{
|
||||
Object: map[string]any{
|
||||
"apiVersion": "operators.coreos.com/v1alpha1",
|
||||
"kind": "Subscription",
|
||||
"metadata": map[string]any{
|
||||
"name": "upgrade-sub",
|
||||
"namespace": "ns1",
|
||||
},
|
||||
"status": map[string]any{
|
||||
"state": "UpgradeAvailable",
|
||||
"conditions": []interface{}{
|
||||
map[string]any{
|
||||
"status": "False",
|
||||
"reason": "CatalogSourcesUnhealthy",
|
||||
"message": "not reachable",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
listKinds := map[schema.GroupVersionResource]string{
|
||||
{Group: "operators.coreos.com", Version: "v1alpha1", Resource: "subscriptions"}: "SubscriptionList",
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
dc := dynamicfake.NewSimpleDynamicClientWithCustomListKinds(scheme, listKinds, ok, bad)
|
||||
|
||||
a := common.Analyzer{
|
||||
Context: context.TODO(),
|
||||
Client: &kubernetes.Client{DynamicClient: dc},
|
||||
}
|
||||
|
||||
res, err := (SubscriptionAnalyzer{}).Analyze(a)
|
||||
if err != nil {
|
||||
t.Fatalf("Analyze error: %v", err)
|
||||
}
|
||||
|
||||
if len(res) != 1 {
|
||||
t.Fatalf("expected 1 result, got %d", len(res))
|
||||
}
|
||||
if res[0].Kind != "Subscription" || !strings.Contains(res[0].Name, "ns1/upgrade-sub") {
|
||||
t.Fatalf("unexpected result: %#v", res[0])
|
||||
}
|
||||
if len(res[0].Error) == 0 || !strings.Contains(res[0].Error[0].Text, "CatalogSourcesUnhealthy") {
|
||||
t.Fatalf("expected 'CatalogSourcesUnhealthy' in failure, got %#v", res[0].Error)
|
||||
}
|
||||
}
|
||||
60
pkg/cache/cache_test.go
vendored
Normal file
60
pkg/cache/cache_test.go
vendored
Normal 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
77
pkg/cache/file_based_test.go
vendored
Normal 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")
|
||||
}
|
||||
17
pkg/cache/interplex_based_test.go
vendored
17
pkg/cache/interplex_based_test.go
vendored
@@ -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 {
|
||||
|
||||
41
pkg/custom/client_test.go
Normal file
41
pkg/custom/client_test.go
Normal 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)
|
||||
}
|
||||
@@ -40,12 +40,20 @@ type AnalyzeRequest struct {
|
||||
WithStats bool `json:"withStats,omitempty"`
|
||||
}
|
||||
|
||||
// AnalyzeResponse represents the output of the analyze tool
|
||||
type AnalyzeResponse struct {
|
||||
Content []struct {
|
||||
Text string `json:"text"`
|
||||
Type string `json:"type"`
|
||||
} `json:"content"`
|
||||
// JSONRPCResponse represents the JSON-RPC response format
|
||||
type JSONRPCResponse struct {
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
ID int `json:"id"`
|
||||
Result struct {
|
||||
Content []struct {
|
||||
Text string `json:"text"`
|
||||
Type string `json:"type"`
|
||||
} `json:"content"`
|
||||
} `json:"result,omitempty"`
|
||||
Error *struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
} `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -65,23 +73,89 @@ func main() {
|
||||
MaxConcurrency: 10,
|
||||
}
|
||||
|
||||
// Convert request to JSON
|
||||
reqJSON, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to marshal request: %v", err)
|
||||
}
|
||||
// Note: req is now used directly in the JSON-RPC request
|
||||
|
||||
// Create HTTP client with timeout
|
||||
client := &http.Client{
|
||||
Timeout: 5 * time.Minute,
|
||||
}
|
||||
|
||||
// Send request to MCP server
|
||||
resp, err := client.Post(
|
||||
fmt.Sprintf("http://localhost:%s/mcp/analyze", *serverPort),
|
||||
// First, initialize the session
|
||||
initRequest := map[string]interface{}{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "initialize",
|
||||
"params": map[string]interface{}{
|
||||
"protocolVersion": "2025-03-26",
|
||||
"capabilities": map[string]interface{}{
|
||||
"tools": map[string]interface{}{},
|
||||
"resources": map[string]interface{}{},
|
||||
"prompts": map[string]interface{}{},
|
||||
},
|
||||
"clientInfo": map[string]interface{}{
|
||||
"name": "k8sgpt-client",
|
||||
"version": "1.0.0",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
initData, err := json.Marshal(initRequest)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to marshal init request: %v", err)
|
||||
}
|
||||
|
||||
// Send initialization request
|
||||
initResp, err := client.Post(
|
||||
fmt.Sprintf("http://localhost:%s/mcp", *serverPort),
|
||||
"application/json",
|
||||
bytes.NewBuffer(reqJSON),
|
||||
bytes.NewBuffer(initData),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to send init request: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := initResp.Body.Close(); err != nil {
|
||||
log.Printf("Error closing init response body: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Extract session ID from response headers
|
||||
sessionID := initResp.Header.Get("Mcp-Session-Id")
|
||||
if sessionID == "" {
|
||||
log.Println("Warning: No session ID received from server")
|
||||
}
|
||||
|
||||
// Create JSON-RPC request for analyze
|
||||
jsonRPCRequest := map[string]interface{}{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 2,
|
||||
"method": "tools/call",
|
||||
"params": map[string]interface{}{
|
||||
"name": "analyze",
|
||||
"arguments": req,
|
||||
},
|
||||
}
|
||||
|
||||
// Convert to JSON
|
||||
jsonRPCData, err := json.Marshal(jsonRPCRequest)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to marshal JSON-RPC request: %v", err)
|
||||
}
|
||||
|
||||
// Create request with session ID if available
|
||||
httpReq, err := http.NewRequest("POST", fmt.Sprintf("http://localhost:%s/mcp", *serverPort), bytes.NewBuffer(jsonRPCData))
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create request: %v", err)
|
||||
}
|
||||
|
||||
httpReq.Header.Set("Content-Type", "application/json")
|
||||
httpReq.Header.Set("Accept", "application/json,text/event-stream")
|
||||
if sessionID != "" {
|
||||
httpReq.Header.Set("Mcp-Session-Id", sessionID)
|
||||
}
|
||||
|
||||
// Send request to MCP server
|
||||
resp, err := client.Do(httpReq)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to send request: %v", err)
|
||||
}
|
||||
@@ -99,15 +173,17 @@ func main() {
|
||||
fmt.Printf("Raw response: %s\n", string(body))
|
||||
|
||||
// Parse response
|
||||
var analyzeResp AnalyzeResponse
|
||||
if err := json.Unmarshal(body, &analyzeResp); err != nil {
|
||||
var jsonRPCResp JSONRPCResponse
|
||||
if err := json.Unmarshal(body, &jsonRPCResp); err != nil {
|
||||
log.Fatalf("Failed to decode response: %v", err)
|
||||
}
|
||||
|
||||
// Print results
|
||||
fmt.Println("Analysis Results:")
|
||||
if len(analyzeResp.Content) > 0 {
|
||||
fmt.Println(analyzeResp.Content[0].Text)
|
||||
if jsonRPCResp.Error != nil {
|
||||
fmt.Printf("Error: %s (code: %d)\n", jsonRPCResp.Error.Message, jsonRPCResp.Error.Code)
|
||||
} else if len(jsonRPCResp.Result.Content) > 0 {
|
||||
fmt.Println(jsonRPCResp.Result.Content[0].Text)
|
||||
} else {
|
||||
fmt.Println("No results returned")
|
||||
}
|
||||
|
||||
@@ -17,88 +17,326 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
schemav1 "buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go/schema/v1"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/analysis"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/server/config"
|
||||
mcp_golang "github.com/metoro-io/mcp-golang"
|
||||
mcp_http "github.com/metoro-io/mcp-golang/transport/http"
|
||||
"github.com/metoro-io/mcp-golang/transport/stdio"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
"github.com/spf13/viper"
|
||||
"go.uber.org/zap"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// MCPServer represents an MCP server for k8sgpt
|
||||
type MCPServer struct {
|
||||
server *mcp_golang.Server
|
||||
port string
|
||||
aiProvider *ai.AIProvider
|
||||
useHTTP bool
|
||||
logger *zap.Logger
|
||||
// K8sGptMCPServer represents an MCP server for k8sgpt
|
||||
type K8sGptMCPServer struct {
|
||||
server *server.MCPServer
|
||||
port string
|
||||
aiProvider *ai.AIProvider
|
||||
useHTTP bool
|
||||
logger *zap.Logger
|
||||
httpServer *server.StreamableHTTPServer
|
||||
stdioServer *server.StdioServer
|
||||
}
|
||||
|
||||
// NewMCPServer creates a new MCP server
|
||||
func NewMCPServer(port string, aiProvider *ai.AIProvider, useHTTP bool, logger *zap.Logger) (*MCPServer, error) {
|
||||
opts := []mcp_golang.ServerOptions{
|
||||
mcp_golang.WithName("k8sgpt"),
|
||||
mcp_golang.WithVersion("1.0.0"),
|
||||
func NewMCPServer(port string, aiProvider *ai.AIProvider, useHTTP bool, logger *zap.Logger) (*K8sGptMCPServer, error) {
|
||||
opts := []server.ServerOption{
|
||||
server.WithToolCapabilities(true),
|
||||
server.WithResourceCapabilities(true, false),
|
||||
server.WithPromptCapabilities(false),
|
||||
}
|
||||
|
||||
var server *mcp_golang.Server
|
||||
if useHTTP {
|
||||
logger.Info("starting MCP server with http transport on port", zap.String("port", port))
|
||||
httpTransport := mcp_http.NewHTTPTransport("/mcp").WithAddr(":" + port)
|
||||
server = mcp_golang.NewServer(httpTransport, opts...)
|
||||
} else {
|
||||
server = mcp_golang.NewServer(stdio.NewStdioServerTransport(), opts...)
|
||||
}
|
||||
|
||||
return &MCPServer{
|
||||
server: server,
|
||||
// Create the MCP server
|
||||
mcpServer := server.NewMCPServer("k8sgpt", "1.0.0", opts...)
|
||||
var k8sGptMCPServer = &K8sGptMCPServer{
|
||||
server: mcpServer,
|
||||
port: port,
|
||||
aiProvider: aiProvider,
|
||||
useHTTP: useHTTP,
|
||||
logger: logger,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Register tools and resources immediately
|
||||
if err := k8sGptMCPServer.registerToolsAndResources(); err != nil {
|
||||
return nil, fmt.Errorf("failed to register tools and resources: %v", err)
|
||||
}
|
||||
|
||||
if useHTTP {
|
||||
// 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...)
|
||||
|
||||
// Launch the HTTP server directly
|
||||
go func() {
|
||||
logger.Info("Starting MCP HTTP server", zap.String("port", port))
|
||||
if err := httpServer.Start(":" + port); err != nil {
|
||||
logger.Fatal("MCP HTTP server failed", zap.Error(err))
|
||||
}
|
||||
}()
|
||||
|
||||
return &K8sGptMCPServer{
|
||||
server: mcpServer,
|
||||
port: port,
|
||||
aiProvider: aiProvider,
|
||||
useHTTP: useHTTP,
|
||||
logger: logger,
|
||||
httpServer: httpServer,
|
||||
}, nil
|
||||
} else {
|
||||
// Create stdio server
|
||||
stdioServer := server.NewStdioServer(mcpServer)
|
||||
|
||||
return &K8sGptMCPServer{
|
||||
server: mcpServer,
|
||||
port: port,
|
||||
aiProvider: aiProvider,
|
||||
useHTTP: useHTTP,
|
||||
logger: logger,
|
||||
stdioServer: stdioServer,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts the MCP server
|
||||
func (s *MCPServer) Start() error {
|
||||
func (s *K8sGptMCPServer) Start() error {
|
||||
if s.server == nil {
|
||||
return fmt.Errorf("server not initialized")
|
||||
}
|
||||
|
||||
// Register analyze tool
|
||||
if err := s.server.RegisterTool("analyze", "Analyze Kubernetes resources", s.handleAnalyze); err != nil {
|
||||
return fmt.Errorf("failed to register analyze tool: %v", err)
|
||||
// Register prompts
|
||||
if err := s.registerPrompts(); err != nil {
|
||||
return fmt.Errorf("failed to register prompts: %v", err)
|
||||
}
|
||||
|
||||
// Register cluster info tool
|
||||
if err := s.server.RegisterTool("cluster-info", "Get Kubernetes cluster information", s.handleClusterInfo); err != nil {
|
||||
return fmt.Errorf("failed to register cluster-info tool: %v", err)
|
||||
}
|
||||
|
||||
// Register config tool
|
||||
if err := s.server.RegisterTool("config", "Configure K8sGPT settings", s.handleConfig); err != nil {
|
||||
return fmt.Errorf("failed to register config tool: %v", err)
|
||||
}
|
||||
|
||||
// Register resources
|
||||
if err := s.registerResources(); err != nil {
|
||||
return fmt.Errorf("failed to register resources: %v", err)
|
||||
}
|
||||
|
||||
// Register prompts
|
||||
if err := s.registerPrompts(); err != nil {
|
||||
return fmt.Errorf("failed to register prompts: %v", err)
|
||||
// Start the server based on transport type
|
||||
if s.useHTTP {
|
||||
// HTTP server is already running in a goroutine
|
||||
return nil
|
||||
} else {
|
||||
// Start stdio server (this will block)
|
||||
return server.ServeStdio(s.server)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *K8sGptMCPServer) registerToolsAndResources() error {
|
||||
// Register analyze tool with proper JSON schema
|
||||
analyzeTool := mcp.NewTool("analyze",
|
||||
mcp.WithDescription("Analyze Kubernetes resources for issues and problems"),
|
||||
mcp.WithString("namespace",
|
||||
mcp.Description("Kubernetes namespace to analyze (empty for all namespaces)"),
|
||||
),
|
||||
mcp.WithString("backend",
|
||||
mcp.Description("AI backend to use for analysis (e.g., openai, azure, localai)"),
|
||||
),
|
||||
mcp.WithBoolean("explain",
|
||||
mcp.Description("Provide detailed explanations for issues"),
|
||||
),
|
||||
mcp.WithArray("filters",
|
||||
mcp.Description("Provide filters to narrow down the analysis (e.g. ['Pods', 'Deployments'])"),
|
||||
// without below line MCP server fails with Google Agent Development Kit (ADK), interestingly works fine with mcpinspector
|
||||
mcp.WithStringItems(),
|
||||
),
|
||||
)
|
||||
s.server.AddTool(analyzeTool, s.handleAnalyze)
|
||||
|
||||
// Register cluster info tool (no parameters needed)
|
||||
clusterInfoTool := mcp.NewTool("cluster-info",
|
||||
mcp.WithDescription("Get Kubernetes cluster information and version"),
|
||||
)
|
||||
s.server.AddTool(clusterInfoTool, s.handleClusterInfo)
|
||||
|
||||
// Register config tool with proper JSON schema
|
||||
configTool := mcp.NewTool("config",
|
||||
mcp.WithDescription("Configure K8sGPT settings including custom analyzers and cache"),
|
||||
mcp.WithObject("customAnalyzers",
|
||||
mcp.Description("Custom analyzer configurations"),
|
||||
mcp.Properties(map[string]any{
|
||||
"name": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Name of the custom analyzer",
|
||||
},
|
||||
"connection": map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{
|
||||
"url": map[string]any{
|
||||
"type": "string",
|
||||
"description": "URL of the custom analyzer service",
|
||||
},
|
||||
"port": map[string]any{
|
||||
"type": "integer",
|
||||
"description": "Port of the custom analyzer service",
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
mcp.WithObject("cache",
|
||||
mcp.Description("Cache configuration"),
|
||||
mcp.Properties(map[string]any{
|
||||
"type": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Cache type (s3, azure, gcs)",
|
||||
"enum": []string{"s3", "azure", "gcs"},
|
||||
},
|
||||
"bucketName": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Bucket name for S3/GCS cache",
|
||||
},
|
||||
"region": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Region for S3/GCS cache",
|
||||
},
|
||||
"endpoint": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Custom endpoint for S3 cache",
|
||||
},
|
||||
"insecure": map[string]any{
|
||||
"type": "boolean",
|
||||
"description": "Use insecure connection for cache",
|
||||
},
|
||||
"storageAccount": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Storage account for Azure cache",
|
||||
},
|
||||
"containerName": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Container name for Azure cache",
|
||||
},
|
||||
"projectId": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Project ID for GCS cache",
|
||||
},
|
||||
}),
|
||||
),
|
||||
)
|
||||
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)
|
||||
|
||||
// Start the server (this will block)
|
||||
if err := s.server.Serve(); err != nil {
|
||||
s.logger.Error("Error starting MCP server", zap.Error(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -116,6 +354,7 @@ type AnalyzeRequest struct {
|
||||
InteractiveMode bool `json:"interactiveMode,omitempty"`
|
||||
CustomHeaders []string `json:"customHeaders,omitempty"`
|
||||
WithStats bool `json:"withStats,omitempty"`
|
||||
Anonymize bool `json:"anonymize,omitempty"`
|
||||
}
|
||||
|
||||
// AnalyzeResponse represents the output of the analyze tool
|
||||
@@ -163,62 +402,74 @@ type ConfigResponse struct {
|
||||
}
|
||||
|
||||
// handleAnalyze handles the analyze tool
|
||||
func (s *MCPServer) handleAnalyze(ctx context.Context, request *AnalyzeRequest) (*mcp_golang.ToolResponse, error) {
|
||||
// Get stored configuration
|
||||
var configAI ai.AIConfiguration
|
||||
if err := viper.UnmarshalKey("ai", &configAI); err != nil {
|
||||
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(fmt.Sprintf("Failed to load AI configuration: %v", err))), nil
|
||||
func (s *K8sGptMCPServer) handleAnalyze(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
|
||||
var req AnalyzeRequest
|
||||
if err := request.BindArguments(&req); err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to parse request arguments: %v", err), nil
|
||||
}
|
||||
// Use stored configuration if not specified in request
|
||||
if request.Backend == "" {
|
||||
if configAI.DefaultProvider != "" {
|
||||
request.Backend = configAI.DefaultProvider
|
||||
} else if len(configAI.Providers) > 0 {
|
||||
request.Backend = configAI.Providers[0].Name
|
||||
|
||||
if req.Backend == "" {
|
||||
if s.aiProvider.Name != "" {
|
||||
req.Backend = s.aiProvider.Name
|
||||
} else {
|
||||
request.Backend = "openai" // fallback default
|
||||
req.Backend = "openai" // fallback default
|
||||
}
|
||||
}
|
||||
|
||||
request.Explain = true
|
||||
// Get stored filters if not specified
|
||||
if len(request.Filters) == 0 {
|
||||
request.Filters = viper.GetStringSlice("active_filters")
|
||||
if len(req.Filters) == 0 {
|
||||
req.Filters = viper.GetStringSlice("active_filters")
|
||||
}
|
||||
|
||||
// Validate MaxConcurrency to prevent excessive memory allocation
|
||||
request.MaxConcurrency = validateMaxConcurrency(request.MaxConcurrency)
|
||||
req.MaxConcurrency = validateMaxConcurrency(req.MaxConcurrency)
|
||||
|
||||
// Create a new analysis with the request parameters
|
||||
analysis, err := analysis.NewAnalysis(
|
||||
request.Backend,
|
||||
request.Language,
|
||||
request.Filters,
|
||||
request.Namespace,
|
||||
request.LabelSelector,
|
||||
request.NoCache,
|
||||
request.Explain,
|
||||
request.MaxConcurrency,
|
||||
request.WithDoc,
|
||||
request.InteractiveMode,
|
||||
request.CustomHeaders,
|
||||
request.WithStats,
|
||||
req.Backend,
|
||||
req.Language,
|
||||
req.Filters,
|
||||
req.Namespace,
|
||||
req.LabelSelector,
|
||||
req.NoCache,
|
||||
req.Explain,
|
||||
req.MaxConcurrency,
|
||||
req.WithDoc,
|
||||
req.InteractiveMode,
|
||||
req.CustomHeaders,
|
||||
req.WithStats,
|
||||
)
|
||||
if err != nil {
|
||||
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(fmt.Sprintf("Failed to create analysis: %v", err))), nil
|
||||
return mcp.NewToolResultErrorf("Failed to create analysis: %v", err), nil
|
||||
}
|
||||
defer analysis.Close()
|
||||
|
||||
// Run the analysis
|
||||
analysis.RunAnalysis()
|
||||
if req.Explain {
|
||||
|
||||
// Get the output
|
||||
output, err := analysis.PrintOutput("json")
|
||||
if err != nil {
|
||||
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(fmt.Sprintf("Failed to print output: %v", err))), nil
|
||||
var output string
|
||||
err := analysis.GetAIResults(output, req.Anonymize)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to get results from AI: %v", err), nil
|
||||
}
|
||||
|
||||
// Convert results to JSON string using PrintOutput
|
||||
outputBytes, err := analysis.PrintOutput("text")
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to convert results to string: %v", err), nil
|
||||
}
|
||||
plainText := stripANSI(string(outputBytes))
|
||||
return mcp.NewToolResultText(plainText), nil
|
||||
} else {
|
||||
// Get the output
|
||||
output, err := analysis.PrintOutput("json")
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to print output: %v", err), nil
|
||||
}
|
||||
return mcp.NewToolResultText(string(output)), nil
|
||||
}
|
||||
|
||||
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(string(output))), nil
|
||||
}
|
||||
|
||||
// validateMaxConcurrency validates and bounds the MaxConcurrency parameter
|
||||
@@ -233,25 +484,31 @@ func validateMaxConcurrency(maxConcurrency int) int {
|
||||
}
|
||||
|
||||
// handleClusterInfo handles the cluster-info tool
|
||||
func (s *MCPServer) handleClusterInfo(ctx context.Context, request *ClusterInfoRequest) (*mcp_golang.ToolResponse, error) {
|
||||
func (s *K8sGptMCPServer) handleClusterInfo(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
// Create a new Kubernetes client
|
||||
client, err := kubernetes.NewClient("", "")
|
||||
if err != nil {
|
||||
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(fmt.Sprintf("failed to create Kubernetes client: %v", err))), nil
|
||||
return mcp.NewToolResultErrorf("failed to create Kubernetes client: %v", err), nil
|
||||
}
|
||||
|
||||
// Get cluster info from the client
|
||||
version, err := client.Client.Discovery().ServerVersion()
|
||||
if err != nil {
|
||||
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(fmt.Sprintf("failed to get cluster version: %v", err))), nil
|
||||
return mcp.NewToolResultErrorf("failed to get cluster version: %v", err), nil
|
||||
}
|
||||
|
||||
info := fmt.Sprintf("Kubernetes %s", version.GitVersion)
|
||||
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(info)), nil
|
||||
return mcp.NewToolResultText(info), nil
|
||||
}
|
||||
|
||||
// handleConfig handles the config tool
|
||||
func (s *MCPServer) handleConfig(ctx context.Context, request *ConfigRequest) (*mcp_golang.ToolResponse, error) {
|
||||
func (s *K8sGptMCPServer) handleConfig(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
// Parse request arguments
|
||||
var req ConfigRequest
|
||||
if err := request.BindArguments(&req); err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to parse request arguments: %v", err), nil
|
||||
}
|
||||
|
||||
// Create a new config handler
|
||||
handler := &config.Handler{}
|
||||
|
||||
@@ -261,8 +518,8 @@ func (s *MCPServer) handleConfig(ctx context.Context, request *ConfigRequest) (*
|
||||
}
|
||||
|
||||
// Add custom analyzers if present
|
||||
if len(request.CustomAnalyzers) > 0 {
|
||||
for _, ca := range request.CustomAnalyzers {
|
||||
if len(req.CustomAnalyzers) > 0 {
|
||||
for _, ca := range req.CustomAnalyzers {
|
||||
addConfigReq.CustomAnalyzers = append(addConfigReq.CustomAnalyzers, &schemav1.CustomAnalyzer{
|
||||
Name: ca.Name,
|
||||
Connection: &schemav1.Connection{
|
||||
@@ -274,31 +531,31 @@ func (s *MCPServer) handleConfig(ctx context.Context, request *ConfigRequest) (*
|
||||
}
|
||||
|
||||
// Add cache configuration if present
|
||||
if request.Cache.Type != "" {
|
||||
if req.Cache.Type != "" {
|
||||
cacheConfig := &schemav1.Cache{}
|
||||
switch request.Cache.Type {
|
||||
switch req.Cache.Type {
|
||||
case "s3":
|
||||
cacheConfig.CacheType = &schemav1.Cache_S3Cache{
|
||||
S3Cache: &schemav1.S3Cache{
|
||||
BucketName: request.Cache.BucketName,
|
||||
Region: request.Cache.Region,
|
||||
Endpoint: request.Cache.Endpoint,
|
||||
Insecure: request.Cache.Insecure,
|
||||
BucketName: req.Cache.BucketName,
|
||||
Region: req.Cache.Region,
|
||||
Endpoint: req.Cache.Endpoint,
|
||||
Insecure: req.Cache.Insecure,
|
||||
},
|
||||
}
|
||||
case "azure":
|
||||
cacheConfig.CacheType = &schemav1.Cache_AzureCache{
|
||||
AzureCache: &schemav1.AzureCache{
|
||||
StorageAccount: request.Cache.StorageAccount,
|
||||
ContainerName: request.Cache.ContainerName,
|
||||
StorageAccount: req.Cache.StorageAccount,
|
||||
ContainerName: req.Cache.ContainerName,
|
||||
},
|
||||
}
|
||||
case "gcs":
|
||||
cacheConfig.CacheType = &schemav1.Cache_GcsCache{
|
||||
GcsCache: &schemav1.GCSCache{
|
||||
BucketName: request.Cache.BucketName,
|
||||
Region: request.Cache.Region,
|
||||
ProjectId: request.Cache.ProjectId,
|
||||
BucketName: req.Cache.BucketName,
|
||||
Region: req.Cache.Region,
|
||||
ProjectId: req.Cache.ProjectId,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -307,27 +564,61 @@ func (s *MCPServer) handleConfig(ctx context.Context, request *ConfigRequest) (*
|
||||
|
||||
// Apply the configuration using the shared function
|
||||
if err := handler.ApplyConfig(ctx, addConfigReq); err != nil {
|
||||
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(fmt.Sprintf("Failed to add config: %v", err))), nil
|
||||
return mcp.NewToolResultErrorf("Failed to add config: %v", err), nil
|
||||
}
|
||||
|
||||
return mcp_golang.NewToolResponse(mcp_golang.NewTextContent("Successfully added configuration")), nil
|
||||
return mcp.NewToolResultText("Successfully added configuration"), nil
|
||||
}
|
||||
|
||||
// registerPrompts registers the prompts for the MCP server
|
||||
func (s *MCPServer) registerPrompts() error {
|
||||
// Register any prompts needed for the MCP server
|
||||
func (s *K8sGptMCPServer) registerPrompts() error {
|
||||
// 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
|
||||
}
|
||||
|
||||
// registerResources registers the resources for the MCP server
|
||||
func (s *MCPServer) registerResources() error {
|
||||
if err := s.server.RegisterResource("cluster-info", "Get cluster information", "Get information about the Kubernetes cluster", "text", s.getClusterInfo); err != nil {
|
||||
return fmt.Errorf("failed to register cluster-info resource: %v", err)
|
||||
}
|
||||
func (s *K8sGptMCPServer) registerResources() error {
|
||||
clusterInfoResource := mcp.NewResource("cluster-info", "cluster-info",
|
||||
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
|
||||
}
|
||||
|
||||
func (s *MCPServer) getClusterInfo(ctx context.Context) (*mcp_golang.ResourceResponse, error) {
|
||||
func (s *K8sGptMCPServer) getClusterInfo(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
|
||||
// Create a new Kubernetes client
|
||||
client, err := kubernetes.NewClient("", "")
|
||||
if err != nil {
|
||||
@@ -346,24 +637,110 @@ func (s *MCPServer) getClusterInfo(ctx context.Context) (*mcp_golang.ResourceRes
|
||||
"gitVersion": version.GitVersion,
|
||||
})
|
||||
if err != nil {
|
||||
return mcp_golang.NewResourceResponse(
|
||||
mcp_golang.NewTextEmbeddedResource(
|
||||
"cluster-info",
|
||||
"Failed to marshal cluster info",
|
||||
"text/plain",
|
||||
),
|
||||
), nil
|
||||
return []mcp.ResourceContents{
|
||||
&mcp.TextResourceContents{
|
||||
URI: "cluster-info",
|
||||
MIMEType: "text/plain",
|
||||
Text: "Failed to marshal cluster info",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return mcp_golang.NewResourceResponse(
|
||||
mcp_golang.NewTextEmbeddedResource(
|
||||
"cluster-info",
|
||||
string(data),
|
||||
"application/json",
|
||||
),
|
||||
), nil
|
||||
|
||||
return []mcp.ResourceContents{
|
||||
&mcp.TextResourceContents{
|
||||
URI: "cluster-info",
|
||||
MIMEType: "application/json",
|
||||
Text: string(data),
|
||||
},
|
||||
}, 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 *MCPServer) Close() error {
|
||||
func (s *K8sGptMCPServer) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// zapLoggerAdapter adapts zap.Logger to the interface expected by mark3labs/mcp-go
|
||||
type zapLoggerAdapter struct {
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func (z *zapLoggerAdapter) Infof(format string, v ...any) {
|
||||
z.logger.Info(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
func (z *zapLoggerAdapter) Errorf(format string, v ...any) {
|
||||
z.logger.Error(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
// stripANSI removes ANSI escape sequences from a string
|
||||
func stripANSI(input string) string {
|
||||
re := regexp.MustCompile(`\x1b\[[0-9;]*m`)
|
||||
return re.ReplaceAllString(input, "")
|
||||
}
|
||||
|
||||
570
pkg/server/mcp_handlers.go
Normal file
570
pkg/server/mcp_handlers.go
Normal file
@@ -0,0 +1,570 @@
|
||||
/*
|
||||
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"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultListLimit is the default maximum number of resources to return
|
||||
DefaultListLimit = 100
|
||||
// MaxListLimit is the maximum allowed limit for list operations
|
||||
MaxListLimit = 1000
|
||||
)
|
||||
|
||||
// resourceLister defines a function that lists Kubernetes resources
|
||||
type resourceLister func(ctx context.Context, client *kubernetes.Client, namespace string, opts metav1.ListOptions) (interface{}, error)
|
||||
|
||||
// resourceGetter defines a function that gets a single Kubernetes resource
|
||||
type resourceGetter func(ctx context.Context, client *kubernetes.Client, namespace, name string) (interface{}, error)
|
||||
|
||||
// resourceRegistry maps resource types to their list and get functions
|
||||
var resourceRegistry = map[string]struct {
|
||||
list resourceLister
|
||||
get resourceGetter
|
||||
}{
|
||||
"pod": {
|
||||
list: func(ctx context.Context, client *kubernetes.Client, namespace string, opts metav1.ListOptions) (interface{}, error) {
|
||||
return client.Client.CoreV1().Pods(namespace).List(ctx, opts)
|
||||
},
|
||||
get: func(ctx context.Context, client *kubernetes.Client, namespace, name string) (interface{}, error) {
|
||||
return client.Client.CoreV1().Pods(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
},
|
||||
},
|
||||
"deployment": {
|
||||
list: func(ctx context.Context, client *kubernetes.Client, namespace string, opts metav1.ListOptions) (interface{}, error) {
|
||||
return client.Client.AppsV1().Deployments(namespace).List(ctx, opts)
|
||||
},
|
||||
get: func(ctx context.Context, client *kubernetes.Client, namespace, name string) (interface{}, error) {
|
||||
return client.Client.AppsV1().Deployments(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
},
|
||||
},
|
||||
"service": {
|
||||
list: func(ctx context.Context, client *kubernetes.Client, namespace string, opts metav1.ListOptions) (interface{}, error) {
|
||||
return client.Client.CoreV1().Services(namespace).List(ctx, opts)
|
||||
},
|
||||
get: func(ctx context.Context, client *kubernetes.Client, namespace, name string) (interface{}, error) {
|
||||
return client.Client.CoreV1().Services(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
},
|
||||
},
|
||||
"node": {
|
||||
list: func(ctx context.Context, client *kubernetes.Client, namespace string, opts metav1.ListOptions) (interface{}, error) {
|
||||
return client.Client.CoreV1().Nodes().List(ctx, opts)
|
||||
},
|
||||
get: func(ctx context.Context, client *kubernetes.Client, namespace, name string) (interface{}, error) {
|
||||
return client.Client.CoreV1().Nodes().Get(ctx, name, metav1.GetOptions{})
|
||||
},
|
||||
},
|
||||
"job": {
|
||||
list: func(ctx context.Context, client *kubernetes.Client, namespace string, opts metav1.ListOptions) (interface{}, error) {
|
||||
return client.Client.BatchV1().Jobs(namespace).List(ctx, opts)
|
||||
},
|
||||
get: func(ctx context.Context, client *kubernetes.Client, namespace, name string) (interface{}, error) {
|
||||
return client.Client.BatchV1().Jobs(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
},
|
||||
},
|
||||
"cronjob": {
|
||||
list: func(ctx context.Context, client *kubernetes.Client, namespace string, opts metav1.ListOptions) (interface{}, error) {
|
||||
return client.Client.BatchV1().CronJobs(namespace).List(ctx, opts)
|
||||
},
|
||||
get: func(ctx context.Context, client *kubernetes.Client, namespace, name string) (interface{}, error) {
|
||||
return client.Client.BatchV1().CronJobs(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
},
|
||||
},
|
||||
"statefulset": {
|
||||
list: func(ctx context.Context, client *kubernetes.Client, namespace string, opts metav1.ListOptions) (interface{}, error) {
|
||||
return client.Client.AppsV1().StatefulSets(namespace).List(ctx, opts)
|
||||
},
|
||||
get: func(ctx context.Context, client *kubernetes.Client, namespace, name string) (interface{}, error) {
|
||||
return client.Client.AppsV1().StatefulSets(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
},
|
||||
},
|
||||
"daemonset": {
|
||||
list: func(ctx context.Context, client *kubernetes.Client, namespace string, opts metav1.ListOptions) (interface{}, error) {
|
||||
return client.Client.AppsV1().DaemonSets(namespace).List(ctx, opts)
|
||||
},
|
||||
get: func(ctx context.Context, client *kubernetes.Client, namespace, name string) (interface{}, error) {
|
||||
return client.Client.AppsV1().DaemonSets(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
},
|
||||
},
|
||||
"replicaset": {
|
||||
list: func(ctx context.Context, client *kubernetes.Client, namespace string, opts metav1.ListOptions) (interface{}, error) {
|
||||
return client.Client.AppsV1().ReplicaSets(namespace).List(ctx, opts)
|
||||
},
|
||||
get: func(ctx context.Context, client *kubernetes.Client, namespace, name string) (interface{}, error) {
|
||||
return client.Client.AppsV1().ReplicaSets(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
},
|
||||
},
|
||||
"configmap": {
|
||||
list: func(ctx context.Context, client *kubernetes.Client, namespace string, opts metav1.ListOptions) (interface{}, error) {
|
||||
return client.Client.CoreV1().ConfigMaps(namespace).List(ctx, opts)
|
||||
},
|
||||
get: func(ctx context.Context, client *kubernetes.Client, namespace, name string) (interface{}, error) {
|
||||
return client.Client.CoreV1().ConfigMaps(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
},
|
||||
},
|
||||
"secret": {
|
||||
list: func(ctx context.Context, client *kubernetes.Client, namespace string, opts metav1.ListOptions) (interface{}, error) {
|
||||
return client.Client.CoreV1().Secrets(namespace).List(ctx, opts)
|
||||
},
|
||||
get: func(ctx context.Context, client *kubernetes.Client, namespace, name string) (interface{}, error) {
|
||||
return client.Client.CoreV1().Secrets(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
},
|
||||
},
|
||||
"ingress": {
|
||||
list: func(ctx context.Context, client *kubernetes.Client, namespace string, opts metav1.ListOptions) (interface{}, error) {
|
||||
return client.Client.NetworkingV1().Ingresses(namespace).List(ctx, opts)
|
||||
},
|
||||
get: func(ctx context.Context, client *kubernetes.Client, namespace, name string) (interface{}, error) {
|
||||
return client.Client.NetworkingV1().Ingresses(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
},
|
||||
},
|
||||
"persistentvolumeclaim": {
|
||||
list: func(ctx context.Context, client *kubernetes.Client, namespace string, opts metav1.ListOptions) (interface{}, error) {
|
||||
return client.Client.CoreV1().PersistentVolumeClaims(namespace).List(ctx, opts)
|
||||
},
|
||||
get: func(ctx context.Context, client *kubernetes.Client, namespace, name string) (interface{}, error) {
|
||||
return client.Client.CoreV1().PersistentVolumeClaims(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
},
|
||||
},
|
||||
"persistentvolume": {
|
||||
list: func(ctx context.Context, client *kubernetes.Client, namespace string, opts metav1.ListOptions) (interface{}, error) {
|
||||
return client.Client.CoreV1().PersistentVolumes().List(ctx, opts)
|
||||
},
|
||||
get: func(ctx context.Context, client *kubernetes.Client, namespace, name string) (interface{}, error) {
|
||||
return client.Client.CoreV1().PersistentVolumes().Get(ctx, name, metav1.GetOptions{})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Resource type aliases for convenience
|
||||
var resourceTypeAliases = map[string]string{
|
||||
"pods": "pod",
|
||||
"deployments": "deployment",
|
||||
"services": "service",
|
||||
"svc": "service",
|
||||
"nodes": "node",
|
||||
"jobs": "job",
|
||||
"cronjobs": "cronjob",
|
||||
"statefulsets": "statefulset",
|
||||
"sts": "statefulset",
|
||||
"daemonsets": "daemonset",
|
||||
"ds": "daemonset",
|
||||
"replicasets": "replicaset",
|
||||
"rs": "replicaset",
|
||||
"configmaps": "configmap",
|
||||
"cm": "configmap",
|
||||
"secrets": "secret",
|
||||
"ingresses": "ingress",
|
||||
"ing": "ingress",
|
||||
"persistentvolumeclaims": "persistentvolumeclaim",
|
||||
"pvc": "persistentvolumeclaim",
|
||||
"persistentvolumes": "persistentvolume",
|
||||
"pv": "persistentvolume",
|
||||
}
|
||||
|
||||
// normalizeResourceType converts resource type variants to canonical form
|
||||
func normalizeResourceType(resourceType string) (string, error) {
|
||||
normalized := strings.ToLower(resourceType)
|
||||
|
||||
// Check if it's an alias
|
||||
if canonical, ok := resourceTypeAliases[normalized]; ok {
|
||||
normalized = canonical
|
||||
}
|
||||
|
||||
// Check if it's a known resource type
|
||||
if _, ok := resourceRegistry[normalized]; !ok {
|
||||
return "", fmt.Errorf("unsupported resource type: %s", resourceType)
|
||||
}
|
||||
|
||||
return normalized, nil
|
||||
}
|
||||
|
||||
// marshalJSON marshals data to JSON with proper error handling
|
||||
func marshalJSON(data interface{}) (string, error) {
|
||||
jsonData, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to marshal JSON: %w", err)
|
||||
}
|
||||
return string(jsonData), nil
|
||||
}
|
||||
|
||||
// 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"`
|
||||
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.ResourceType == "" {
|
||||
return mcp.NewToolResultErrorf("resourceType is required"), nil
|
||||
}
|
||||
|
||||
// Normalize and validate resource type
|
||||
resourceType, err := normalizeResourceType(req.ResourceType)
|
||||
if err != nil {
|
||||
supportedTypes := make([]string, 0, len(resourceRegistry))
|
||||
for key := range resourceRegistry {
|
||||
supportedTypes = append(supportedTypes, key)
|
||||
}
|
||||
return mcp.NewToolResultErrorf("%v. Supported types: %v", err, supportedTypes), nil
|
||||
}
|
||||
|
||||
// Set default and validate limit
|
||||
if req.Limit == 0 {
|
||||
req.Limit = DefaultListLimit
|
||||
} else if req.Limit > MaxListLimit {
|
||||
req.Limit = MaxListLimit
|
||||
}
|
||||
|
||||
client, err := kubernetes.NewClient("", "")
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to create Kubernetes client: %v", err), nil
|
||||
}
|
||||
|
||||
listOptions := metav1.ListOptions{
|
||||
LabelSelector: req.LabelSelector,
|
||||
Limit: req.Limit,
|
||||
}
|
||||
|
||||
// Get the list function from registry
|
||||
listFunc := resourceRegistry[resourceType].list
|
||||
result, err := listFunc(ctx, client, req.Namespace, listOptions)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to list %s: %v", resourceType, err), nil
|
||||
}
|
||||
|
||||
// Extract items from the result (all list types have an Items field)
|
||||
resultJSON, err := marshalJSON(result)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to serialize result: %v", err), nil
|
||||
}
|
||||
|
||||
return mcp.NewToolResultText(resultJSON), 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
|
||||
}
|
||||
|
||||
if req.ResourceType == "" {
|
||||
return mcp.NewToolResultErrorf("resourceType is required"), nil
|
||||
}
|
||||
if req.Name == "" {
|
||||
return mcp.NewToolResultErrorf("name is required"), nil
|
||||
}
|
||||
|
||||
// Normalize and validate resource type
|
||||
resourceType, err := normalizeResourceType(req.ResourceType)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("%v", err), nil
|
||||
}
|
||||
|
||||
client, err := kubernetes.NewClient("", "")
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to create Kubernetes client: %v", err), nil
|
||||
}
|
||||
|
||||
// Get the get function from registry
|
||||
getFunc := resourceRegistry[resourceType].get
|
||||
result, err := getFunc(ctx, client, req.Namespace, req.Name)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to get %s '%s': %v", resourceType, req.Name, err), nil
|
||||
}
|
||||
|
||||
resultJSON, err := marshalJSON(result)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to serialize result: %v", err), nil
|
||||
}
|
||||
|
||||
return mcp.NewToolResultText(resultJSON), 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
|
||||
}
|
||||
|
||||
resultJSON, err := marshalJSON(namespaces.Items)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to serialize result: %v", err), nil
|
||||
}
|
||||
|
||||
return mcp.NewToolResultText(resultJSON), 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 = DefaultListLimit
|
||||
} else if req.Limit > MaxListLimit {
|
||||
req.Limit = MaxListLimit
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
resultJSON, err := marshalJSON(filteredEvents)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to serialize result: %v", err), nil
|
||||
}
|
||||
|
||||
return mcp.NewToolResultText(resultJSON), 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.PodName == "" {
|
||||
return mcp.NewToolResultErrorf("podName is required"), nil
|
||||
}
|
||||
if req.Namespace == "" {
|
||||
return mcp.NewToolResultErrorf("namespace is required"), 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,
|
||||
}
|
||||
|
||||
resultJSON, err := marshalJSON(result)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to serialize result: %v", err), nil
|
||||
}
|
||||
|
||||
return mcp.NewToolResultText(resultJSON), 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
|
||||
}
|
||||
|
||||
if len(req.Filters) == 0 {
|
||||
return mcp.NewToolResultErrorf("filters array is required and cannot be empty"), nil
|
||||
}
|
||||
|
||||
activeFilters := viper.GetStringSlice("active_filters")
|
||||
addedFilters := []string{}
|
||||
|
||||
for _, filter := range req.Filters {
|
||||
if !contains(activeFilters, filter) {
|
||||
activeFilters = append(activeFilters, filter)
|
||||
addedFilters = append(addedFilters, filter)
|
||||
}
|
||||
}
|
||||
|
||||
viper.Set("active_filters", activeFilters)
|
||||
if err := viper.WriteConfig(); err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to save configuration: %v", err), nil
|
||||
}
|
||||
|
||||
if len(addedFilters) == 0 {
|
||||
return mcp.NewToolResultText("All specified filters were already active"), nil
|
||||
}
|
||||
|
||||
return mcp.NewToolResultText(fmt.Sprintf("Successfully added filters: %v", addedFilters)), 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
|
||||
}
|
||||
|
||||
if len(req.Filters) == 0 {
|
||||
return mcp.NewToolResultErrorf("filters array is required and cannot be empty"), nil
|
||||
}
|
||||
|
||||
activeFilters := viper.GetStringSlice("active_filters")
|
||||
newFilters := []string{}
|
||||
removedFilters := []string{}
|
||||
|
||||
for _, filter := range activeFilters {
|
||||
if !contains(req.Filters, filter) {
|
||||
newFilters = append(newFilters, filter)
|
||||
} else {
|
||||
removedFilters = append(removedFilters, filter)
|
||||
}
|
||||
}
|
||||
|
||||
viper.Set("active_filters", newFilters)
|
||||
if err := viper.WriteConfig(); err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to save configuration: %v", err), nil
|
||||
}
|
||||
|
||||
if len(removedFilters) == 0 {
|
||||
return mcp.NewToolResultText("None of the specified filters were active"), nil
|
||||
}
|
||||
|
||||
return mcp.NewToolResultText(fmt.Sprintf("Successfully removed filters: %v", removedFilters)), 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,
|
||||
})
|
||||
}
|
||||
|
||||
resultJSON, err := marshalJSON(result)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to serialize result: %v", err), nil
|
||||
}
|
||||
|
||||
return mcp.NewToolResultText(resultJSON), 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
170
pkg/server/mcp_prompts.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -3,6 +3,7 @@ package server
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
@@ -78,14 +79,14 @@ func TestMCPServerCreation(t *testing.T) {
|
||||
}
|
||||
|
||||
// Test HTTP mode
|
||||
mcpServer, err := NewMCPServer("8089", aiProvider, true, logger)
|
||||
mcpServer, err := NewMCPServer("8088", aiProvider, true, logger)
|
||||
assert.NoError(t, err, "Should be able to create MCP server with HTTP transport")
|
||||
assert.NotNil(t, mcpServer, "MCP server should not be nil")
|
||||
assert.True(t, mcpServer.useHTTP, "MCP server should be in HTTP mode")
|
||||
assert.Equal(t, "8089", mcpServer.port, "Port should be set correctly")
|
||||
assert.Equal(t, "8088", mcpServer.port, "Port should be set correctly")
|
||||
|
||||
// Test stdio mode
|
||||
mcpServerStdio, err := NewMCPServer("8089", aiProvider, false, logger)
|
||||
mcpServerStdio, err := NewMCPServer("8088", aiProvider, false, logger)
|
||||
assert.NoError(t, err, "Should be able to create MCP server with stdio transport")
|
||||
assert.NotNil(t, mcpServerStdio, "MCP server should not be nil")
|
||||
assert.False(t, mcpServerStdio.useHTTP, "MCP server should be in stdio mode")
|
||||
@@ -107,27 +108,83 @@ func TestMCPServerBasicHTTP(t *testing.T) {
|
||||
Model: "test-model",
|
||||
}
|
||||
|
||||
mcpServer, err := NewMCPServer("8089", aiProvider, true, logger)
|
||||
mcpServer, err := NewMCPServer("8091", aiProvider, true, logger)
|
||||
assert.NoError(t, err, "Should be able to create MCP server")
|
||||
|
||||
// Start the MCP server in a goroutine
|
||||
go func() {
|
||||
err := mcpServer.Start()
|
||||
// Note: Start() might return an error when the server is stopped, which is expected
|
||||
if err != nil {
|
||||
logger.Info("MCP server stopped", zap.Error(err))
|
||||
}
|
||||
}()
|
||||
// For HTTP mode, the server is already started in NewMCPServer
|
||||
// No need to call Start() as it's already running in a goroutine
|
||||
|
||||
// Wait for the server to start
|
||||
err = waitForPort("localhost:8089", 10*time.Second)
|
||||
err = waitForPort("localhost:8091", 10*time.Second)
|
||||
if err != nil {
|
||||
t.Skipf("MCP server did not start within timeout: %v", err)
|
||||
}
|
||||
|
||||
// Test basic connectivity to the MCP endpoint
|
||||
// The MCP HTTP transport uses a single POST endpoint for all requests
|
||||
resp, err := http.Post("http://localhost:8089/mcp", "application/json", bytes.NewBufferString(`{"jsonrpc":"2.0","id":1,"method":"tools/list"}`))
|
||||
// First, initialize the session
|
||||
initRequest := `{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "initialize",
|
||||
"params": {
|
||||
"protocolVersion": "2025-03-26",
|
||||
"capabilities": {
|
||||
"tools": {},
|
||||
"resources": {},
|
||||
"prompts": {}
|
||||
},
|
||||
"clientInfo": {
|
||||
"name": "test-client",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
initResp, err := http.Post("http://localhost:8091/mcp", "application/json", bytes.NewBufferString(initRequest))
|
||||
if err != nil {
|
||||
t.Logf("Initialize request failed: %v", err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err := initResp.Body.Close(); err != nil {
|
||||
t.Logf("Error closing init response body: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Read initialization response
|
||||
initBody, err := io.ReadAll(initResp.Body)
|
||||
if err != nil {
|
||||
t.Logf("Failed to read init response body: %v", err)
|
||||
} else {
|
||||
t.Logf("Init response status: %d, body: %s", initResp.StatusCode, string(initBody))
|
||||
}
|
||||
|
||||
// Extract session ID from response headers if present
|
||||
sessionID := initResp.Header.Get("Mcp-Session-Id")
|
||||
if sessionID == "" {
|
||||
t.Logf("No session ID in response headers")
|
||||
}
|
||||
|
||||
// Now test tools/list with session ID if available
|
||||
headers := map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json,text/event-stream",
|
||||
}
|
||||
if sessionID != "" {
|
||||
headers["Mcp-Session-Id"] = sessionID
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", "http://localhost:8091/mcp", bytes.NewBufferString(`{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}`))
|
||||
if err != nil {
|
||||
t.Logf("Failed to create request: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
for key, value := range headers {
|
||||
req.Header.Set(key, value)
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Logf("MCP endpoint test skipped (server might not be fully ready): %v", err)
|
||||
return
|
||||
@@ -139,6 +196,14 @@ func TestMCPServerBasicHTTP(t *testing.T) {
|
||||
}
|
||||
}()
|
||||
|
||||
// Read response body for debugging
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Logf("Failed to read response body: %v", err)
|
||||
} else {
|
||||
t.Logf("Response status: %d, body: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
// Accept both 200 and 404 as valid responses (404 means endpoint not implemented)
|
||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNotFound {
|
||||
t.Errorf("MCP endpoint returned unexpected status: %d", resp.StatusCode)
|
||||
@@ -168,13 +233,8 @@ func TestMCPServerToolCall(t *testing.T) {
|
||||
mcpServer, err := NewMCPServer("8090", aiProvider, true, logger)
|
||||
assert.NoError(t, err, "Should be able to create MCP server")
|
||||
|
||||
// Start the MCP server in a goroutine
|
||||
go func() {
|
||||
err := mcpServer.Start()
|
||||
if err != nil {
|
||||
logger.Info("MCP server stopped", zap.Error(err))
|
||||
}
|
||||
}()
|
||||
// For HTTP mode, the server is already started in NewMCPServer
|
||||
// No need to call Start() as it's already running in a goroutine
|
||||
|
||||
// Wait for the server to start
|
||||
err = waitForPort("localhost:8090", 10*time.Second)
|
||||
@@ -182,6 +242,39 @@ func TestMCPServerToolCall(t *testing.T) {
|
||||
t.Skipf("MCP server did not start within timeout: %v", err)
|
||||
}
|
||||
|
||||
// First, initialize the session
|
||||
initRequest := `{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "initialize",
|
||||
"params": {
|
||||
"protocolVersion": "2025-03-26",
|
||||
"capabilities": {
|
||||
"tools": {},
|
||||
"resources": {},
|
||||
"prompts": {}
|
||||
},
|
||||
"clientInfo": {
|
||||
"name": "test-client",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
initResp, err := http.Post("http://localhost:8090/mcp", "application/json", bytes.NewBufferString(initRequest))
|
||||
if err != nil {
|
||||
t.Logf("Initialize request failed: %v", err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err := initResp.Body.Close(); err != nil {
|
||||
t.Logf("Error closing init response body: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Extract session ID from response headers if present
|
||||
sessionID := initResp.Header.Get("Mcp-Session-Id")
|
||||
|
||||
// Test calling the analyze tool with proper JSON-RPC format
|
||||
analyzeRequest := `{
|
||||
"jsonrpc": "2.0",
|
||||
@@ -199,7 +292,21 @@ func TestMCPServerToolCall(t *testing.T) {
|
||||
}
|
||||
}`
|
||||
|
||||
resp, err := http.Post("http://localhost:8090/mcp", "application/json", bytes.NewBufferString(analyzeRequest))
|
||||
// Create request with session ID if available
|
||||
req, err := http.NewRequest("POST", "http://localhost:8090/mcp", bytes.NewBufferString(analyzeRequest))
|
||||
if err != nil {
|
||||
t.Logf("Failed to create request: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Accept", "application/json,text/event-stream")
|
||||
if sessionID != "" {
|
||||
req.Header.Set("Mcp-Session-Id", sessionID)
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Logf("Analyze tool call test skipped (server might not be fully ready): %v", err)
|
||||
return
|
||||
|
||||
@@ -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"))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user