Compare commits

..

2 Commits

Author SHA1 Message Date
Thomas Schuetz
8e1920e0aa fix: ignore old client go packages in renovate
Signed-off-by: Thomas Schuetz <thomas.schuetz@t-sc.eu>
2023-04-28 17:44:19 +02:00
Thomas Schuetz
8e8f77d01d docs: remove issue templates to use org wide ones
Signed-off-by: Thomas Schuetz <thomas.schuetz@t-sc.eu>
2023-04-27 16:25:10 +02:00
118 changed files with 1377 additions and 9755 deletions

View File

@@ -13,7 +13,7 @@ on:
- "**.md"
env:
GO_VERSION: "~1.21"
GO_VERSION: "~1.20"
IMAGE_NAME: "k8sgpt"
defaults:
run:
@@ -33,7 +33,7 @@ jobs:
steps:
- name: Check out code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3
- name: Extract branch name
id: extract_branch
@@ -70,14 +70,14 @@ jobs:
RELEASE_REGISTRY: "localhost:5000/k8sgpt"
steps:
- name: Check out code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3
uses: docker/setup-buildx-action@4b4e9c3e2d4531116a6f8ba8e71fc6e2cb6e6c8c # v2
- name: Build Docker Image
uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 # v4
with:
context: .
platforms: linux/amd64
@@ -96,7 +96,7 @@ jobs:
outputs: type=docker,dest=/tmp/${{ env.IMAGE_NAME }}-image.tar
- name: Upload image as artifact
uses: actions/upload-artifact@1eb3cb2b3e0f29609092a73eb033bb759a334595 # v4
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3
with:
name: ${{ env.IMAGE_NAME }}-image.tar
path: /tmp/${{ env.IMAGE_NAME }}-image.tar
@@ -115,10 +115,10 @@ jobs:
contents: read # Needed for checking out the repository
steps:
- name: Check out code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3
- name: Login to GitHub Container Registry
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2
with:
registry: "ghcr.io"
username: ${{ github.actor }}
@@ -126,10 +126,10 @@ jobs:
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3
uses: docker/setup-buildx-action@4b4e9c3e2d4531116a6f8ba8e71fc6e2cb6e6c8c # v2
- name: Build Docker Image
uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 # v4
with:
context: .
file: ./container/Dockerfile

View File

@@ -9,10 +9,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3
- name: golangci-lint
uses: reviewdog/action-golangci-lint@24d4af2fc93f5b2b296229e8b0c0f658d25707af # v2
uses: reviewdog/action-golangci-lint@53f8eabb87b40b1a2c63ec75b0d418bd0f4aa919 # v2
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
reporter: github-pr-check

View File

@@ -23,9 +23,9 @@ jobs:
# Release-please creates a PR that tracks all changes
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- uses: google-github-actions/release-please-action@cc61a07e2da466bebbc19b3a7dd01d6aecb20d1e # v4.0.2
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3
- uses: google-github-actions/release-please-action@c078ea33917ab8cfa5300e48f4b7e6b16606aede # v3
id: release
with:
command: manifest
@@ -41,17 +41,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4
with:
go-version: '1.21'
go-version: '1.20'
- name: Download Syft
uses: anchore/sbom-action/download-syft@c7f031d9249a826a082ea14c79d3b686a51d485a # v0.15.3
uses: anchore/sbom-action/download-syft@422cb34a0f8b599678c41b21163ea6088edb2624 # v0.14.1
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5
uses: goreleaser/goreleaser-action@f82d6c1c344bcacabba2c841718984797f664a6b # v4
with:
# either 'goreleaser' (default) or 'goreleaser-pro'
distribution: goreleaser
@@ -74,23 +74,23 @@ jobs:
IMAGE_NAME: k8sgpt
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3
with:
submodules: recursive
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3
uses: docker/setup-buildx-action@4b4e9c3e2d4531116a6f8ba8e71fc6e2cb6e6c8c # v2
- name: Login to GitHub Container Registry
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2
with:
registry: "ghcr.io"
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker Image
uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 # v4
with:
context: .
file: ./container/Dockerfile
@@ -104,7 +104,7 @@ jobs:
cache-to: type=gha,scope=${{ github.ref_name }}-${{ env.IMAGE_TAG }}
- name: Generate SBOM
uses: anchore/sbom-action@c7f031d9249a826a082ea14c79d3b686a51d485a # v0.15.3
uses: anchore/sbom-action@422cb34a0f8b599678c41b21163ea6088edb2624 # v0.14.1
with:
image: ${{ env.IMAGE_TAG }}
artifact-name: sbom-${{ env.IMAGE_NAME }}
@@ -114,4 +114,4 @@ jobs:
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
with:
tag_name: ${{ needs.release-please.outputs.tag_name }}
files: ./sbom-${{ env.IMAGE_NAME }}.spdx.json
files: ./sbom-${{ env.IMAGE_NAME }}.spdx.json

View File

@@ -16,7 +16,7 @@ jobs:
pull-requests: read # Needed for reading prs
steps:
- name: Validate Pull Request
uses: amannn/action-semantic-pull-request@e9fabac35e210fea40ca5b14c0da95a099eff26f # v5.4.0
uses: amannn/action-semantic-pull-request@c3cd5d1ea3580753008872425915e343e351ab54 # v5.2.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:

View File

@@ -9,19 +9,19 @@ on:
- main
env:
GO_VERSION: "~1.21"
GO_VERSION: "~1.20"
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3
- name: Set up Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4
with:
go-version: ${{ env.GO_VERSION }}
- name: Run test
run: go test ./...
- name: Test
run: go test -v ./...

3
.gitignore vendored
View File

@@ -1,8 +1,5 @@
.idea
__debug*
.DS_Store
k8sgpt*
!charts/k8sgpt
*.vscode
dist/

View File

@@ -15,15 +15,13 @@ builds:
- darwin
ldflags:
- -s -w -X main.version={{.Version}}
- -s -w -X main.commit={{.ShortCommit}}
- -s -w -X main.Date={{.CommitDate}}
nfpms:
- file_name_template: '{{ .ProjectName }}_{{ .Arch }}'
homepage: https://k8sgpt.ai
description: >-
K8sGPT is a tool for scanning your kubernetes clusters, diagnosing and triaging issues in simple english. It has SRE experience codified into its analyzers and helps to pull out the most relevant information to enrich it with AI.
license: "Apache-2.0"
license: "MIT"
formats:
- deb
- rpm
@@ -57,7 +55,7 @@ archives:
brews:
- name: k8sgpt
homepage: https://k8sgpt.ai
repository:
tap:
owner: k8sgpt-ai
name: homebrew-k8sgpt

View File

@@ -1 +1 @@
{".":"0.3.26"}
{".":"0.2.8"}

View File

@@ -1,730 +1,5 @@
# Changelog
## [0.3.26](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.25...v0.3.26) (2024-01-14)
### Features
* initial Prometheus analyzers ([#855](https://github.com/k8sgpt-ai/k8sgpt/issues/855)) ([45fa827](https://github.com/k8sgpt-ai/k8sgpt/commit/45fa827c046b91d901a08bec1a892d9c0917f350))
* interactive mode ([#854](https://github.com/k8sgpt-ai/k8sgpt/issues/854)) ([9da75e0](https://github.com/k8sgpt-ai/k8sgpt/commit/9da75e02bc17146898377e4f90b7f59c5a8e0eee))
* unify aiClientName const for all providers ([#848](https://github.com/k8sgpt-ai/k8sgpt/issues/848)) ([5c17c24](https://github.com/k8sgpt-ai/k8sgpt/commit/5c17c240550609d9fb7771fe67fe1ab19660b4da))
### Bug Fixes
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.16 ([#847](https://github.com/k8sgpt-ai/k8sgpt/issues/847)) ([ce4910b](https://github.com/k8sgpt-ai/k8sgpt/commit/ce4910bc5d064f80076877d7a096fff903308b63))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.17 ([#852](https://github.com/k8sgpt-ai/k8sgpt/issues/852)) ([85ebd12](https://github.com/k8sgpt-ai/k8sgpt/commit/85ebd12c30d369c5ef9a42b5a834d091523a7b6e))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.18 ([#856](https://github.com/k8sgpt-ai/k8sgpt/issues/856)) ([4106d39](https://github.com/k8sgpt-ai/k8sgpt/commit/4106d39c322940413ebfd9ac0bf6f5bd31830e93))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.19 ([#859](https://github.com/k8sgpt-ai/k8sgpt/issues/859)) ([6a2f315](https://github.com/k8sgpt-ai/k8sgpt/commit/6a2f315b2f4344f2924b7915e8a1393f9732a1e9))
* **deps:** update module github.com/sashabaranov/go-openai to v1.17.11 ([#853](https://github.com/k8sgpt-ai/k8sgpt/issues/853)) ([1979c86](https://github.com/k8sgpt-ai/k8sgpt/commit/1979c86d0f59921d55cd4229a37d604a6f1dc578))
* **deps:** update module github.com/sashabaranov/go-openai to v1.17.11 ([#861](https://github.com/k8sgpt-ai/k8sgpt/issues/861)) ([40b5b7e](https://github.com/k8sgpt-ai/k8sgpt/commit/40b5b7e185c8d335bdefb131988b9900ad26bac3))
* **deps:** update module gopkg.in/yaml.v2 to v3 ([#864](https://github.com/k8sgpt-ai/k8sgpt/issues/864)) ([36ba6c5](https://github.com/k8sgpt-ai/k8sgpt/commit/36ba6c5147a9ed75c14dbba4bc06cae903e651a4))
* **deps:** update module gopkg.in/yaml.v2 to v3 ([#865](https://github.com/k8sgpt-ai/k8sgpt/issues/865)) ([c55025d](https://github.com/k8sgpt-ai/k8sgpt/commit/c55025d04ebf9da0f6092aabb0b043ccef05164c))
### Other
* **deps:** update actions/upload-artifact digest to 1eb3cb2 ([#867](https://github.com/k8sgpt-ai/k8sgpt/issues/867)) ([4ce56f3](https://github.com/k8sgpt-ai/k8sgpt/commit/4ce56f38b4338a6a2fe69f588b0f17e0b54d0ae6))
* **deps:** update anchore/sbom-action action to v0.15.3 ([#850](https://github.com/k8sgpt-ai/k8sgpt/issues/850)) ([12f764d](https://github.com/k8sgpt-ai/k8sgpt/commit/12f764d5846accbd987d40f69a153dceb9954f39))
### Docs
* adjusted README information about providers ([#844](https://github.com/k8sgpt-ai/k8sgpt/issues/844)) ([745e960](https://github.com/k8sgpt-ai/k8sgpt/commit/745e960f492e6dd0e50aa4a1ce7239c677025024))
## [0.3.25](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.24...v0.3.25) (2024-01-05)
### Features
* added Google GenAI client; simplified IAI/clients API surface. ([#829](https://github.com/k8sgpt-ai/k8sgpt/issues/829)) ([e7d4149](https://github.com/k8sgpt-ai/k8sgpt/commit/e7d41496ddaa145c70079852da8b2ce3b3b7289f))
* code_cov badge ([#821](https://github.com/k8sgpt-ai/k8sgpt/issues/821)) ([fcd29a5](https://github.com/k8sgpt-ai/k8sgpt/commit/fcd29a547d73ba48935762e2f568f5755f5c6ed3))
* coverage reports ([#819](https://github.com/k8sgpt-ai/k8sgpt/issues/819)) ([3d0ba3e](https://github.com/k8sgpt-ai/k8sgpt/commit/3d0ba3e78cabaf5f1262c5b5b16ebabad974fa87))
### Bug Fixes
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.10 ([#811](https://github.com/k8sgpt-ai/k8sgpt/issues/811)) ([e5cc4a2](https://github.com/k8sgpt-ai/k8sgpt/commit/e5cc4a28cb3682e7094e6ceddf91b65da991ddb6))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.12 ([#813](https://github.com/k8sgpt-ai/k8sgpt/issues/813)) ([91613ba](https://github.com/k8sgpt-ai/k8sgpt/commit/91613baa5cc5244c93deb344abcdd905802eef30))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.14 ([#822](https://github.com/k8sgpt-ai/k8sgpt/issues/822)) ([526e22f](https://github.com/k8sgpt-ai/k8sgpt/commit/526e22f88b8de15eceb10965b045ef0366ff2d6c))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.15 ([#835](https://github.com/k8sgpt-ai/k8sgpt/issues/835)) ([e78ff05](https://github.com/k8sgpt-ai/k8sgpt/commit/e78ff054190cd54cabe17d77ac69443e517f1e55))
* **deps:** update module github.com/prometheus/client_golang to v1.18.0 ([#814](https://github.com/k8sgpt-ai/k8sgpt/issues/814)) ([6eb8f67](https://github.com/k8sgpt-ai/k8sgpt/commit/6eb8f6793ed989ba3ac7ed00336345f68b09bf45))
* **deps:** update module github.com/sashabaranov/go-openai to v1.17.10 ([#824](https://github.com/k8sgpt-ai/k8sgpt/issues/824)) ([4314804](https://github.com/k8sgpt-ai/k8sgpt/commit/4314804ca7e782f5149dc2078ba9c859edc4688a))
* **deps:** update module golang.org/x/term to v0.16.0 ([#831](https://github.com/k8sgpt-ai/k8sgpt/issues/831)) ([4de989c](https://github.com/k8sgpt-ai/k8sgpt/commit/4de989c803ee43a02d75112d1b3a54daee3dd9af))
* **deps:** update module google.golang.org/api to v0.155.0 ([#836](https://github.com/k8sgpt-ai/k8sgpt/issues/836)) ([105a239](https://github.com/k8sgpt-ai/k8sgpt/commit/105a239d94384f4096c01d9978564040773ab56e))
* no explain case, improved readability. ([#825](https://github.com/k8sgpt-ai/k8sgpt/issues/825)) ([035348d](https://github.com/k8sgpt-ai/k8sgpt/commit/035348d8a0d290ac26b42425945eaafe038cedc5))
### Other
* added basic server startup test ([#817](https://github.com/k8sgpt-ai/k8sgpt/issues/817)) ([3e7cea7](https://github.com/k8sgpt-ai/k8sgpt/commit/3e7cea7bd39253718bc3d2f8b10ac5fc9b98cbc2))
* **deps:** pin codecov/codecov-action action to eaaf4be ([#820](https://github.com/k8sgpt-ai/k8sgpt/issues/820)) ([2f0f2df](https://github.com/k8sgpt-ai/k8sgpt/commit/2f0f2dfa8a5957cb8b10864c14d7883158723a6a))
* **deps:** update anchore/sbom-action action to v0.15.2 ([#823](https://github.com/k8sgpt-ai/k8sgpt/issues/823)) ([70c6892](https://github.com/k8sgpt-ai/k8sgpt/commit/70c68929d8d963c0bd17390c76e366d4339f56b9))
* lint fixes ([#833](https://github.com/k8sgpt-ai/k8sgpt/issues/833)) ([a7e9b48](https://github.com/k8sgpt-ai/k8sgpt/commit/a7e9b486bad7c2d62878e470a755d1fef3803680))
* remove code cov ([#832](https://github.com/k8sgpt-ai/k8sgpt/issues/832)) ([a774265](https://github.com/k8sgpt-ai/k8sgpt/commit/a77426593d7f3a8cfa810336ff08a2266db7fb4f))
### Dependency Updates
* go module bump to fix CVE: GHSA-45x7-px36-x8w8 & GHSA-7ww5-4wqc-m92c ([#810](https://github.com/k8sgpt-ai/k8sgpt/issues/810)) ([b17fd7c](https://github.com/k8sgpt-ai/k8sgpt/commit/b17fd7c98644afa70d414fcb32e49e61e1c831ad))
## [0.3.24](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.23...v0.3.24) (2023-12-23)
### Features
* add last termination state when pod is in CrashloopBackoff ([#792](https://github.com/k8sgpt-ai/k8sgpt/issues/792)) ([ff4aaf7](https://github.com/k8sgpt-ai/k8sgpt/commit/ff4aaf7c328a58fcad8e4fb0f93ea543725eedd5))
* Add license scan report and status ([#796](https://github.com/k8sgpt-ai/k8sgpt/issues/796)) ([343aec8](https://github.com/k8sgpt-ai/k8sgpt/commit/343aec8f0455c9461eb8d495ca5bd446b4bad667))
* version upgrade to 1.21 ([#798](https://github.com/k8sgpt-ai/k8sgpt/issues/798)) ([c23f24d](https://github.com/k8sgpt-ai/k8sgpt/commit/c23f24de2e79347e4f5465e28af34e138cc13231))
### Bug Fixes
* added the ability to set the trivy variables by the user ([#797](https://github.com/k8sgpt-ai/k8sgpt/issues/797)) ([928b39a](https://github.com/k8sgpt-ai/k8sgpt/commit/928b39a7283ee274dd517e727624eceb3795594d))
* **deps:** update module cloud.google.com/go/storage to v1.36.0 ([#805](https://github.com/k8sgpt-ai/k8sgpt/issues/805)) ([390f309](https://github.com/k8sgpt-ai/k8sgpt/commit/390f30908800dfe21e2c1660139b0bd9d36b34d6))
* **deps:** update module github.com/aquasecurity/trivy-operator to v0.17.1 ([#780](https://github.com/k8sgpt-ai/k8sgpt/issues/780)) ([71f36bd](https://github.com/k8sgpt-ai/k8sgpt/commit/71f36bdb0b3729c4357299b7d03829dd5b6a69ec))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.6 ([#783](https://github.com/k8sgpt-ai/k8sgpt/issues/783)) ([1b386f6](https://github.com/k8sgpt-ai/k8sgpt/commit/1b386f64f2863d8a49f423ad571cba009807bc55))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.7 ([#804](https://github.com/k8sgpt-ai/k8sgpt/issues/804)) ([3c6c759](https://github.com/k8sgpt-ai/k8sgpt/commit/3c6c7597e014bfd68794b1764c3a8902e8a798ea))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.8 ([#807](https://github.com/k8sgpt-ai/k8sgpt/issues/807)) ([93b5ca1](https://github.com/k8sgpt-ai/k8sgpt/commit/93b5ca1985c3730592388ba6fc32ecca9b806888))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.9 ([#808](https://github.com/k8sgpt-ai/k8sgpt/issues/808)) ([130e4c2](https://github.com/k8sgpt-ai/k8sgpt/commit/130e4c2efd0e5b34cdc84c357c6c1f3987cf7c35))
* **deps:** update module github.com/azure/azure-sdk-for-go/sdk/storage/azblob to v1.2.1 ([#801](https://github.com/k8sgpt-ai/k8sgpt/issues/801)) ([aa05756](https://github.com/k8sgpt-ai/k8sgpt/commit/aa057565b5c971c493443f3ede4aed8f8a6399f7))
* **deps:** update module github.com/mittwald/go-helm-client to v0.12.5 ([#802](https://github.com/k8sgpt-ai/k8sgpt/issues/802)) ([4a7bad3](https://github.com/k8sgpt-ai/k8sgpt/commit/4a7bad313b66750bd830413b7fef005580ad843c))
* **deps:** update module github.com/sashabaranov/go-openai to v1.17.9 ([#772](https://github.com/k8sgpt-ai/k8sgpt/issues/772)) ([13d64a5](https://github.com/k8sgpt-ai/k8sgpt/commit/13d64a58750c7262c07042b557fbf2c4a511b777))
* **deps:** update module github.com/spf13/viper to v1.18.2 ([#787](https://github.com/k8sgpt-ai/k8sgpt/issues/787)) ([8dea617](https://github.com/k8sgpt-ai/k8sgpt/commit/8dea6170a2c00c03f08f25e4f0a232be617536f1))
* **deps:** update module google.golang.org/api to v0.154.0 ([#779](https://github.com/k8sgpt-ai/k8sgpt/issues/779)) ([78f7f2b](https://github.com/k8sgpt-ai/k8sgpt/commit/78f7f2ba85fd357cab13ccc15e9e767e8611773a))
* **deps:** update module google.golang.org/grpc to v1.60.1 ([#790](https://github.com/k8sgpt-ai/k8sgpt/issues/790)) ([5d54c3f](https://github.com/k8sgpt-ai/k8sgpt/commit/5d54c3f840a9ce002606b6601187e69fb62f8a28))
* **deps:** update module helm.sh/helm/v3 to v3.13.3 ([#803](https://github.com/k8sgpt-ai/k8sgpt/issues/803)) ([a8e1932](https://github.com/k8sgpt-ai/k8sgpt/commit/a8e193212222811f3a278df6056dd2165c4323bd))
* lowercase logs before running regex matching in LogAnalyzer ([#794](https://github.com/k8sgpt-ai/k8sgpt/issues/794)) ([03b63be](https://github.com/k8sgpt-ai/k8sgpt/commit/03b63befa247ac84b795a0ec8d5280196b8d570d))
### Other
* **deps:** update actions/setup-go action to v5 ([#788](https://github.com/k8sgpt-ai/k8sgpt/issues/788)) ([d00ed33](https://github.com/k8sgpt-ai/k8sgpt/commit/d00ed33678b1560a3996f1d735d84ca0ca05c0b0))
* **deps:** update actions/upload-artifact action to v4 ([#806](https://github.com/k8sgpt-ai/k8sgpt/issues/806)) ([d6fb648](https://github.com/k8sgpt-ai/k8sgpt/commit/d6fb648e23c1ed1e4680fc4b7b4e96501f50ad48))
* **deps:** update anchore/sbom-action action to v0.15.1 ([#784](https://github.com/k8sgpt-ai/k8sgpt/issues/784)) ([6473a2b](https://github.com/k8sgpt-ai/k8sgpt/commit/6473a2b532491b707b3af922fc2198e626ebf219))
* **deps:** update google-github-actions/release-please-action action to v4 ([#782](https://github.com/k8sgpt-ai/k8sgpt/issues/782)) ([2c28c55](https://github.com/k8sgpt-ai/k8sgpt/commit/2c28c555cf4e891b90ebd9e9eae1cd8724e9886f))
* **deps:** update google-github-actions/release-please-action action to v4.0.2 ([#800](https://github.com/k8sgpt-ai/k8sgpt/issues/800)) ([be4b0bb](https://github.com/k8sgpt-ai/k8sgpt/commit/be4b0bb3c24e04d35f40d16fd8e94ddbc8457ca6))
### Refactoring
* replace rest client with controller-runtime clientset for Trivy analyzers ([#776](https://github.com/k8sgpt-ai/k8sgpt/issues/776)) ([1d19628](https://github.com/k8sgpt-ai/k8sgpt/commit/1d196286b75f0ea6c068e8bdb01455fb36c52432))
## [0.3.23](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.22...v0.3.23) (2023-11-24)
### Features
* add Gateway analysers ([#764](https://github.com/k8sgpt-ai/k8sgpt/issues/764)) ([ec08cac](https://github.com/k8sgpt-ai/k8sgpt/commit/ec08cac21496b34b123b75b06d9283eb6539e890))
### Bug Fixes
* **deps:** update module github.com/aws/aws-sdk-go to v1.48.3 ([#768](https://github.com/k8sgpt-ai/k8sgpt/issues/768)) ([b1c791a](https://github.com/k8sgpt-ai/k8sgpt/commit/b1c791a396b7287ef916e8f8d382a0e14ba39949))
* **deps:** update module github.com/mittwald/go-helm-client to v0.12.4 ([#767](https://github.com/k8sgpt-ai/k8sgpt/issues/767)) ([dca5b47](https://github.com/k8sgpt-ai/k8sgpt/commit/dca5b4710d1bb35dfc3346219d3bddb7c726300e))
## [0.3.22](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.21...v0.3.22) (2023-11-21)
### Features
* rework cache package - add gcs cache - add cache purge command ([#750](https://github.com/k8sgpt-ai/k8sgpt/issues/750)) ([12146bf](https://github.com/k8sgpt-ai/k8sgpt/commit/12146bf356a3b26176c47e3a013a713fd14f346d))
### Bug Fixes
* cover more error reason messages ([#759](https://github.com/k8sgpt-ai/k8sgpt/issues/759)) ([5b27c3e](https://github.com/k8sgpt-ai/k8sgpt/commit/5b27c3e352701819f1d0449df9acf706040f1f13))
* **deps:** update kubernetes packages to v0.28.4 ([#756](https://github.com/k8sgpt-ai/k8sgpt/issues/756)) ([24132c2](https://github.com/k8sgpt-ai/k8sgpt/commit/24132c2d87024157009589cf2bd410bac2a26241))
* **deps:** update module cloud.google.com/go/storage to v1.35.1 ([#762](https://github.com/k8sgpt-ai/k8sgpt/issues/762)) ([58d182e](https://github.com/k8sgpt-ai/k8sgpt/commit/58d182e94f75f9b035a9e45159fa87ce8a57de38))
* **deps:** update module github.com/aquasecurity/trivy-operator to v0.16.4 ([#676](https://github.com/k8sgpt-ai/k8sgpt/issues/676)) ([4531278](https://github.com/k8sgpt-ai/k8sgpt/commit/45312788c3c15e141027c3fc8e428cfaa71d3ace))
* **deps:** update module github.com/aws/aws-sdk-go to v1.47.10 ([#751](https://github.com/k8sgpt-ai/k8sgpt/issues/751)) ([2aa31bc](https://github.com/k8sgpt-ai/k8sgpt/commit/2aa31bc66d239906b1047f53bcaa58b0c30a2856))
* **deps:** update module github.com/aws/aws-sdk-go to v1.47.11 ([#752](https://github.com/k8sgpt-ai/k8sgpt/issues/752)) ([531fa79](https://github.com/k8sgpt-ai/k8sgpt/commit/531fa79ed640846b177c516559dc82f088fa940f))
* **deps:** update module github.com/aws/aws-sdk-go to v1.48.0 ([#754](https://github.com/k8sgpt-ai/k8sgpt/issues/754)) ([e2bb567](https://github.com/k8sgpt-ai/k8sgpt/commit/e2bb567d2f8d59a904583309c2774d4174eb367f))
* **deps:** update module github.com/aws/aws-sdk-go to v1.48.1 ([#766](https://github.com/k8sgpt-ai/k8sgpt/issues/766)) ([16469c0](https://github.com/k8sgpt-ai/k8sgpt/commit/16469c01c962fd5bfa4ad11dd88a41f3e00e4a0d))
* **deps:** update module github.com/sashabaranov/go-openai to v1.17.6 ([#749](https://github.com/k8sgpt-ai/k8sgpt/issues/749)) ([84df364](https://github.com/k8sgpt-ai/k8sgpt/commit/84df3640bc114bb2c768f158d3575732103ff799))
* **deps:** update module github.com/sashabaranov/go-openai to v1.17.7 ([#753](https://github.com/k8sgpt-ai/k8sgpt/issues/753)) ([9971699](https://github.com/k8sgpt-ai/k8sgpt/commit/9971699fcf42b3309449d81875d45180f723de8d))
* **deps:** update module github.com/sashabaranov/go-openai to v1.17.8 ([#761](https://github.com/k8sgpt-ai/k8sgpt/issues/761)) ([beaa532](https://github.com/k8sgpt-ai/k8sgpt/commit/beaa53251c8201028db83d60f208e2b0658c93d8))
* **deps:** update module google.golang.org/api to v0.151.0 ([#763](https://github.com/k8sgpt-ai/k8sgpt/issues/763)) ([3e3f6a9](https://github.com/k8sgpt-ai/k8sgpt/commit/3e3f6a903a81d9622660f5adf9cae7d22a5c99f4))
* show trivy as active when activated with --no-install flag ([#675](https://github.com/k8sgpt-ai/k8sgpt/issues/675)) ([7368271](https://github.com/k8sgpt-ai/k8sgpt/commit/73682717eda4fa2e0cbc6311d5c97e01e0f2673c))
### Other
* **deps:** update anchore/sbom-action action to v0.15.0 ([#765](https://github.com/k8sgpt-ai/k8sgpt/issues/765)) ([cf1e243](https://github.com/k8sgpt-ai/k8sgpt/commit/cf1e243708ab406f070da3f96be1fc60b7ce2ea4))
* **deps:** update docker/build-push-action digest to 4a13e50 ([#760](https://github.com/k8sgpt-ai/k8sgpt/issues/760)) ([b5853de](https://github.com/k8sgpt-ai/k8sgpt/commit/b5853de8a6fcd17b1c1a4c53dbe3ffc82b83f72f))
## [0.3.21](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.20...v0.3.21) (2023-11-12)
### Features
* auth remove: add -b flag ([#711](https://github.com/k8sgpt-ai/k8sgpt/issues/711)) ([9dadd18](https://github.com/k8sgpt-ai/k8sgpt/commit/9dadd186c8d03a4284faff3f0842d6e2d00ebbb8))
* log analyzer ([#744](https://github.com/k8sgpt-ai/k8sgpt/issues/744)) ([d365886](https://github.com/k8sgpt-ai/k8sgpt/commit/d365886753f785bd58118c03510696318ea47941))
### Bug Fixes
* **deps:** update module github.com/aws/aws-sdk-go to v1.47.6 ([#728](https://github.com/k8sgpt-ai/k8sgpt/issues/728)) ([bb21ce8](https://github.com/k8sgpt-ai/k8sgpt/commit/bb21ce80c782e011dfa1f808ccdd82ae748bfed8))
* **deps:** update module github.com/aws/aws-sdk-go to v1.47.8 ([#741](https://github.com/k8sgpt-ai/k8sgpt/issues/741)) ([d359caa](https://github.com/k8sgpt-ai/k8sgpt/commit/d359caaab6bdb42a54d305be2f4cd8452f512bb8))
* **deps:** update module github.com/aws/aws-sdk-go to v1.47.9 ([#743](https://github.com/k8sgpt-ai/k8sgpt/issues/743)) ([45ebad7](https://github.com/k8sgpt-ai/k8sgpt/commit/45ebad7b4d80d93920d5fbad9f42c8fcd45218bd))
* **deps:** update module github.com/fatih/color to v1.16.0 ([#734](https://github.com/k8sgpt-ai/k8sgpt/issues/734)) ([8ab26d9](https://github.com/k8sgpt-ai/k8sgpt/commit/8ab26d96cec73369ecf014d50fccc26afe15fa44))
* **deps:** update module github.com/sashabaranov/go-openai to v1.17.3 ([#737](https://github.com/k8sgpt-ai/k8sgpt/issues/737)) ([48486e9](https://github.com/k8sgpt-ai/k8sgpt/commit/48486e96274a5e52a03cef00bd531148e27b38c5))
* **deps:** update module github.com/sashabaranov/go-openai to v1.17.5 ([#742](https://github.com/k8sgpt-ai/k8sgpt/issues/742)) ([3bff9cb](https://github.com/k8sgpt-ai/k8sgpt/commit/3bff9cbe7bb3afb7212735eb91902fd83d3cbb8c))
* **deps:** update module github.com/schollz/progressbar/v3 to v3.14.1 ([#738](https://github.com/k8sgpt-ai/k8sgpt/issues/738)) ([05f444d](https://github.com/k8sgpt-ai/k8sgpt/commit/05f444dec1f234c191e25f71f3eab4838eb2477a))
* **deps:** update module github.com/spf13/cobra to v1.8.0 ([#732](https://github.com/k8sgpt-ai/k8sgpt/issues/732)) ([19e502a](https://github.com/k8sgpt-ai/k8sgpt/commit/19e502a841e0463b682b0c6b8291f10aee616d7e))
* **deps:** update module helm.sh/helm/v3 to v3.13.2 ([#740](https://github.com/k8sgpt-ai/k8sgpt/issues/740)) ([6a665f0](https://github.com/k8sgpt-ai/k8sgpt/commit/6a665f05d782ba9c3051df7a15ff304c89cb34f4))
### Other
* **deps:** pin google-github-actions/release-please-action action to db8f2c6 ([#747](https://github.com/k8sgpt-ai/k8sgpt/issues/747)) ([4408110](https://github.com/k8sgpt-ai/k8sgpt/commit/4408110b1a4835bb237b3d5674d6fa8a13f0181b))
* **deps:** update google-github-actions/release-please-action digest to 4c5670f ([#721](https://github.com/k8sgpt-ai/k8sgpt/issues/721)) ([9c518ba](https://github.com/k8sgpt-ai/k8sgpt/commit/9c518badf53e4ccd9c2f9251cead4692602c0762))
* **deps:** update google-github-actions/release-please-action digest to db8f2c6 ([#736](https://github.com/k8sgpt-ai/k8sgpt/issues/736)) ([fdb2934](https://github.com/k8sgpt-ai/k8sgpt/commit/fdb2934e8fd02bcb4e47b34c1eca5b099f462faa))
* enable automerge for renovate ([#745](https://github.com/k8sgpt-ai/k8sgpt/issues/745)) ([66ebb88](https://github.com/k8sgpt-ai/k8sgpt/commit/66ebb88efe1ad5ecae75a5299f58a1e68179b515))
* pin release-please version ([#746](https://github.com/k8sgpt-ai/k8sgpt/issues/746)) ([c4925b2](https://github.com/k8sgpt-ai/k8sgpt/commit/c4925b2170546d0d86b77d2a13c13d4907e2e3d6))
### Dependency Updates
* bump docker fixes CVE GHSA-jq35-85cj-fj4p ([#733](https://github.com/k8sgpt-ai/k8sgpt/issues/733)) ([120027e](https://github.com/k8sgpt-ai/k8sgpt/commit/120027e3cbec2535f0b6cc8d8db1dc27dd9f3ec6))
## [0.3.20](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.19...v0.3.20) (2023-11-05)
### Features
* amazonsagemaker AI provider ([#731](https://github.com/k8sgpt-ai/k8sgpt/issues/731)) ([ccef7f6](https://github.com/k8sgpt-ai/k8sgpt/commit/ccef7f617004723b37d1e8ffb011398005e0b392))
### Bug Fixes
* **deps:** update module github.com/aws/aws-sdk-go to v1.47.1 ([#724](https://github.com/k8sgpt-ai/k8sgpt/issues/724)) ([0136b8f](https://github.com/k8sgpt-ai/k8sgpt/commit/0136b8f543a7052e967e29691afe1aab8e5fae1b))
* **deps:** update module github.com/azure/azure-sdk-for-go/sdk/storage/azblob to v1.2.0 ([#723](https://github.com/k8sgpt-ai/k8sgpt/issues/723)) ([16b229d](https://github.com/k8sgpt-ai/k8sgpt/commit/16b229d5478085655041ff0230d2542c4c0c7ce9))
* **deps:** update module google.golang.org/grpc to v1.59.0 ([#713](https://github.com/k8sgpt-ai/k8sgpt/issues/713)) ([901c5ec](https://github.com/k8sgpt-ai/k8sgpt/commit/901c5ec18858f2f7fd385ff20aef77d203748c93))
* **deps:** update module helm.sh/helm/v3 to v3.13.1 ([#706](https://github.com/k8sgpt-ai/k8sgpt/issues/706)) ([40133ad](https://github.com/k8sgpt-ai/k8sgpt/commit/40133adaedff3862199e00f62877a88fcffa67c5))
* ensure ingress HTTP rule exists to prevent panic ([#726](https://github.com/k8sgpt-ai/k8sgpt/issues/726)) ([37721b5](https://github.com/k8sgpt-ai/k8sgpt/commit/37721b5dd77d66edfb7e8377b2b96470b8a21d1b))
### Other
* **deps:** update amannn/action-semantic-pull-request action to v5.4.0 ([#729](https://github.com/k8sgpt-ai/k8sgpt/issues/729)) ([188a8a2](https://github.com/k8sgpt-ai/k8sgpt/commit/188a8a2cd5e25b35446e2eab46279a0ba3976af3))
## [0.3.19](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.18...v0.3.19) (2023-10-28)
### Features
* add amazonbedrock ([#718](https://github.com/k8sgpt-ai/k8sgpt/issues/718)) ([f1a7801](https://github.com/k8sgpt-ai/k8sgpt/commit/f1a7801e9e6a7e4a5310622951dfba3ba3acd047))
* add Azure remote cache ([#690](https://github.com/k8sgpt-ai/k8sgpt/issues/690)) ([23ac52d](https://github.com/k8sgpt-ai/k8sgpt/commit/23ac52d5ffc0b2ebb7516b070fa740108cb4299a))
### Bug Fixes
* **deps:** update kubernetes packages to v0.28.3 ([#715](https://github.com/k8sgpt-ai/k8sgpt/issues/715)) ([7e73f8a](https://github.com/k8sgpt-ai/k8sgpt/commit/7e73f8afbce7ba0e9de432671b88c01fcfe28c3a))
* **deps:** update module github.com/aws/aws-sdk-go to v1.45.25 ([#707](https://github.com/k8sgpt-ai/k8sgpt/issues/707)) ([3ebc867](https://github.com/k8sgpt-ai/k8sgpt/commit/3ebc86772dc8f8cb2d2246724f5fd05d1e931512))
* **deps:** update module github.com/aws/aws-sdk-go to v1.45.26 ([#709](https://github.com/k8sgpt-ai/k8sgpt/issues/709)) ([c977528](https://github.com/k8sgpt-ai/k8sgpt/commit/c977528ec7839902570785e0803f6c6b83a0a69d))
* **deps:** update module github.com/aws/aws-sdk-go to v1.46.5 ([#712](https://github.com/k8sgpt-ai/k8sgpt/issues/712)) ([63a2260](https://github.com/k8sgpt-ai/k8sgpt/commit/63a226065c8068f9bdc0aa791a325fa10bba3fcc))
* **deps:** update module github.com/azure/azure-sdk-for-go/sdk/azidentity to v1.4.0 ([#722](https://github.com/k8sgpt-ai/k8sgpt/issues/722)) ([0e7219a](https://github.com/k8sgpt-ai/k8sgpt/commit/0e7219a36aaa718b7d86adf0a218a521bfac119b))
* **deps:** update module github.com/sashabaranov/go-openai to v1.16.0 ([#703](https://github.com/k8sgpt-ai/k8sgpt/issues/703)) ([b5facd6](https://github.com/k8sgpt-ai/k8sgpt/commit/b5facd64a340a96d38faf045bbb889b928ef08a1))
* **deps:** update module github.com/spf13/viper to v1.17.0 ([#700](https://github.com/k8sgpt-ai/k8sgpt/issues/700)) ([184d148](https://github.com/k8sgpt-ai/k8sgpt/commit/184d1481081f4297bec21fbd60d7eff1964944ae))
* **deps:** update module google.golang.org/grpc to v1.58.3 ([#704](https://github.com/k8sgpt-ai/k8sgpt/issues/704)) ([1d7360c](https://github.com/k8sgpt-ai/k8sgpt/commit/1d7360c0ae4dab376872acc71dc68d59eb4d9752))
### Other
* **deps:** update actions/checkout digest to b4ffde6 ([#719](https://github.com/k8sgpt-ai/k8sgpt/issues/719)) ([a77bd41](https://github.com/k8sgpt-ai/k8sgpt/commit/a77bd410489e624d29ccc8fd45a004f6844b3620))
* **deps:** update module oras.land/oras-go to v1.2.4 ([#665](https://github.com/k8sgpt-ai/k8sgpt/issues/665)) ([4af0ad0](https://github.com/k8sgpt-ai/k8sgpt/commit/4af0ad0303d9b0ffb43f1e87fb5abe279d9a8724))
## [0.3.18](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.17...v0.3.18) (2023-10-12)
### Features
* adding temperature to server mode ([#705](https://github.com/k8sgpt-ai/k8sgpt/issues/705)) ([539ca3b](https://github.com/k8sgpt-ai/k8sgpt/commit/539ca3b78f96694c11f788255d3b83d2fb335df4))
### Bug Fixes
* **deps:** update module buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go to v1.3.0-20231002095256-194bc640518b.1 ([#692](https://github.com/k8sgpt-ai/k8sgpt/issues/692)) ([4d4e33b](https://github.com/k8sgpt-ai/k8sgpt/commit/4d4e33bea9cc4f5f9bf5379db5b890d9ba86e0a9))
* **deps:** update module buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go to v1.31.0-20231002095256-194bc640518b.1 ([#693](https://github.com/k8sgpt-ai/k8sgpt/issues/693)) ([20e6bd8](https://github.com/k8sgpt-ai/k8sgpt/commit/20e6bd816f636d4e4c8274d417870ec28fdd8a56))
* **deps:** update module github.com/aws/aws-sdk-go to v1.45.20 ([#685](https://github.com/k8sgpt-ai/k8sgpt/issues/685)) ([2494946](https://github.com/k8sgpt-ai/k8sgpt/commit/2494946dc867a532460bd6aac74dfb7da5184c1c))
* **deps:** update module github.com/aws/aws-sdk-go to v1.45.21 ([#696](https://github.com/k8sgpt-ai/k8sgpt/issues/696)) ([95c8cc0](https://github.com/k8sgpt-ai/k8sgpt/commit/95c8cc0afb0bb7b99784dcc5ba155f94b5a7dbdf))
* **deps:** update module github.com/aws/aws-sdk-go to v1.45.22 ([#697](https://github.com/k8sgpt-ai/k8sgpt/issues/697)) ([923a8c1](https://github.com/k8sgpt-ai/k8sgpt/commit/923a8c13c06b152d04e8b00ab002e2036bf12740))
* **deps:** update module github.com/aws/aws-sdk-go to v1.45.23 ([#699](https://github.com/k8sgpt-ai/k8sgpt/issues/699)) ([3f36a44](https://github.com/k8sgpt-ai/k8sgpt/commit/3f36a4441532e3d0ac1bd9d00fc738d4902b23a8))
* **deps:** update module github.com/aws/aws-sdk-go to v1.45.24 ([#701](https://github.com/k8sgpt-ai/k8sgpt/issues/701)) ([6d3038b](https://github.com/k8sgpt-ai/k8sgpt/commit/6d3038b0e8336235dc6a2fdb69d2381790331596))
* **deps:** update module github.com/prometheus/client_golang to v1.17.0 ([#687](https://github.com/k8sgpt-ai/k8sgpt/issues/687)) ([9597002](https://github.com/k8sgpt-ai/k8sgpt/commit/95970027237e0079ed1f66dc9655fa01b181f4d7))
* **deps:** update module github.com/sashabaranov/go-openai to v1.15.4 ([#689](https://github.com/k8sgpt-ai/k8sgpt/issues/689)) ([f11d314](https://github.com/k8sgpt-ai/k8sgpt/commit/f11d3149b228b643155ed66c189cb0f8a4dd5a0f))
* **deps:** update module helm.sh/helm/v3 to v3.13.0 ([#688](https://github.com/k8sgpt-ai/k8sgpt/issues/688)) ([87c8bce](https://github.com/k8sgpt-ai/k8sgpt/commit/87c8bcea4becd165aeb0ac98d79df7dab9c37ee3))
* security warning around printing provider details in https://github.com/k8sgpt-ai/k8sgpt/security/code-scanning/1 ([#695](https://github.com/k8sgpt-ai/k8sgpt/issues/695)) ([85ce557](https://github.com/k8sgpt-ai/k8sgpt/commit/85ce55768199f90b1d2a5118ec2621ea5c7a7a67))
### Other
* **deps:** update amannn/action-semantic-pull-request action to v5.3.0 ([#683](https://github.com/k8sgpt-ai/k8sgpt/issues/683)) ([c5a8c46](https://github.com/k8sgpt-ai/k8sgpt/commit/c5a8c462989c097bf37ac48ea4f1a9010285042c))
* fixing default model issue ([#702](https://github.com/k8sgpt-ai/k8sgpt/issues/702)) ([2a34ff2](https://github.com/k8sgpt-ai/k8sgpt/commit/2a34ff24d1f391270ae42531807cb1422880ad27))
## [0.3.17](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.16...v0.3.17) (2023-09-28)
### Features
* added create namespace on deploy ([#673](https://github.com/k8sgpt-ai/k8sgpt/issues/673)) ([820e475](https://github.com/k8sgpt-ai/k8sgpt/commit/820e4755a54ecab3b5d800017bf6948dc9212825))
* integration refactor ([#684](https://github.com/k8sgpt-ai/k8sgpt/issues/684)) ([69fe2db](https://github.com/k8sgpt-ai/k8sgpt/commit/69fe2db8acb795add27f04c1c8ee8d05819300ac))
* update readme with new analyzers ([#671](https://github.com/k8sgpt-ai/k8sgpt/issues/671)) ([cad605a](https://github.com/k8sgpt-ai/k8sgpt/commit/cad605af462ce8b02ffc279ea847e41b7a64196f))
### Bug Fixes
* **deps:** update kubernetes packages to v0.28.2 ([#607](https://github.com/k8sgpt-ai/k8sgpt/issues/607)) ([ddeff9f](https://github.com/k8sgpt-ai/k8sgpt/commit/ddeff9fae4e80d1452893c59b89742633eb6b51b))
* **deps:** update module github.com/aws/aws-sdk-go to v1.45.14 ([#672](https://github.com/k8sgpt-ai/k8sgpt/issues/672)) ([1da4b7c](https://github.com/k8sgpt-ai/k8sgpt/commit/1da4b7c8f0eee877d5b76a7dd9abda7631d922f3))
* **deps:** update module github.com/aws/aws-sdk-go to v1.45.16 ([#682](https://github.com/k8sgpt-ai/k8sgpt/issues/682)) ([e1a42ff](https://github.com/k8sgpt-ai/k8sgpt/commit/e1a42ff3bcb3ddea71df2a5b5288eade024684dc))
* **deps:** update module github.com/google/gnostic to v0.7.0 ([#679](https://github.com/k8sgpt-ai/k8sgpt/issues/679)) ([901ffb8](https://github.com/k8sgpt-ai/k8sgpt/commit/901ffb8df451ce41e6dc96da61deab987e51b6df))
* **deps:** update module google.golang.org/grpc to v1.58.2 ([#680](https://github.com/k8sgpt-ai/k8sgpt/issues/680)) ([402e97d](https://github.com/k8sgpt-ai/k8sgpt/commit/402e97d05ea33879d997d98019b72da0f1074fc7))
### Other
* **deps:** update actions/checkout digest to 8ade135 ([#681](https://github.com/k8sgpt-ai/k8sgpt/issues/681)) ([aa9e6a3](https://github.com/k8sgpt-ai/k8sgpt/commit/aa9e6a3549877260423462c35ebbdfd95381be2c))
## [0.3.16](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.15...v0.3.16) (2023-09-19)
### Features
* lists activate integrations ([#669](https://github.com/k8sgpt-ai/k8sgpt/issues/669)) ([844ff1f](https://github.com/k8sgpt-ai/k8sgpt/commit/844ff1fc78e7c35837c08b72bd2c19e92698d53d))
* openAI explicit value for maxToken and temperature ([#659](https://github.com/k8sgpt-ai/k8sgpt/issues/659)) ([f55946d](https://github.com/k8sgpt-ai/k8sgpt/commit/f55946d60ebc7725aba6702570ca1cb5ba978d78))
* serve/integration capability ([#645](https://github.com/k8sgpt-ai/k8sgpt/issues/645)) ([ab064b9](https://github.com/k8sgpt-ai/k8sgpt/commit/ab064b940cdb39a1588816221b20191e68263c61))
### Bug Fixes
* **deps:** update module buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go to v1.3.0-20230830164712-dc062a152c20.1 ([#617](https://github.com/k8sgpt-ai/k8sgpt/issues/617)) ([d6b7b81](https://github.com/k8sgpt-ai/k8sgpt/commit/d6b7b818aef1b7775d1e76231077b74481546c56))
* **deps:** update module github.com/aws/aws-sdk-go to v1.45.10 ([#657](https://github.com/k8sgpt-ai/k8sgpt/issues/657)) ([0325724](https://github.com/k8sgpt-ai/k8sgpt/commit/03257246589ebbb22961e13394e49b52cb056e38))
* **deps:** update module github.com/aws/aws-sdk-go to v1.45.11 ([#662](https://github.com/k8sgpt-ai/k8sgpt/issues/662)) ([1b80b0c](https://github.com/k8sgpt-ai/k8sgpt/commit/1b80b0ce95f39c1cf27ad8bbb05a7fed10322114))
* **deps:** update module github.com/aws/aws-sdk-go to v1.45.12 ([#666](https://github.com/k8sgpt-ai/k8sgpt/issues/666)) ([b4656f5](https://github.com/k8sgpt-ai/k8sgpt/commit/b4656f533bdf39d12b223158bf41087076fa6c9a))
* **deps:** update module github.com/sashabaranov/go-openai to v1.15.3 ([#636](https://github.com/k8sgpt-ai/k8sgpt/issues/636)) ([54caff8](https://github.com/k8sgpt-ai/k8sgpt/commit/54caff837dc25ae594c6cd0e1bd0b31b1612cf73))
* **deps:** update module go.uber.org/zap to v1.26.0 ([#658](https://github.com/k8sgpt-ai/k8sgpt/issues/658)) ([f76b572](https://github.com/k8sgpt-ai/k8sgpt/commit/f76b57265432a704c3fc5aa67b0d569179b4ef03))
* **deps:** update module google.golang.org/grpc to v1.58.0 ([#635](https://github.com/k8sgpt-ai/k8sgpt/issues/635)) ([d58e002](https://github.com/k8sgpt-ai/k8sgpt/commit/d58e002d7dc55cc759402fcadb03af921cd30dc3))
* **deps:** update module google.golang.org/grpc to v1.58.1 ([#656](https://github.com/k8sgpt-ai/k8sgpt/issues/656)) ([abfb584](https://github.com/k8sgpt-ai/k8sgpt/commit/abfb58432fbd1168db13880e5b9dbcbdde70f147))
* emergency fix for bad package revision in go mod ([#663](https://github.com/k8sgpt-ai/k8sgpt/issues/663)) ([2472da1](https://github.com/k8sgpt-ai/k8sgpt/commit/2472da167300a831dc5b45f7fc0169a0b5b1ccb7))
* pdb panic error guard ([#664](https://github.com/k8sgpt-ai/k8sgpt/issues/664)) ([3277b2a](https://github.com/k8sgpt-ai/k8sgpt/commit/3277b2ad4b27ade9bd7da07f5fc8d8f074355177))
* respect namespace scope in trivy analyzer ([#661](https://github.com/k8sgpt-ai/k8sgpt/issues/661)) ([6481590](https://github.com/k8sgpt-ai/k8sgpt/commit/6481590b29b80391ea1c9298cae5d8f0a4ae7354))
* use default values when adding auth ([#568](https://github.com/k8sgpt-ai/k8sgpt/issues/568)) ([7461a74](https://github.com/k8sgpt-ai/k8sgpt/commit/7461a748f8e994e58ac4f56fd9919b1744bd7366)), closes [#567](https://github.com/k8sgpt-ai/k8sgpt/issues/567)
### Other
* **deps:** update actions/upload-artifact digest to a8a3f3a ([#633](https://github.com/k8sgpt-ai/k8sgpt/issues/633)) ([4bfc7f9](https://github.com/k8sgpt-ai/k8sgpt/commit/4bfc7f996c851adadc5ab0754da6852979084e9d))
* **deps:** update reviewdog/action-golangci-lint digest to 24d4af2 ([#642](https://github.com/k8sgpt-ai/k8sgpt/issues/642)) ([f607360](https://github.com/k8sgpt-ai/k8sgpt/commit/f60736035b2601650f4b3ee352f16d1e57d6ec64))
## [0.3.15](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.14...v0.3.15) (2023-09-14)
### Features
* show each ConfigAuditReport check ([#646](https://github.com/k8sgpt-ai/k8sgpt/issues/646)) ([230eace](https://github.com/k8sgpt-ai/k8sgpt/commit/230eace18737a81e4c023826ffef1a9b1e17d4fd))
### Bug Fixes
* defer to service analyser when selectors are missing ([#652](https://github.com/k8sgpt-ai/k8sgpt/issues/652)) ([6c5a062](https://github.com/k8sgpt-ai/k8sgpt/commit/6c5a0628e4a8c493beae85049448e6e6588d63be))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.333 ([#611](https://github.com/k8sgpt-ai/k8sgpt/issues/611)) ([96d97cf](https://github.com/k8sgpt-ai/k8sgpt/commit/96d97cfa30c4d3c75facda3d3016c080dfa86eaa))
* **deps:** update module github.com/aws/aws-sdk-go to v1.45.0 ([#618](https://github.com/k8sgpt-ai/k8sgpt/issues/618)) ([632fc9a](https://github.com/k8sgpt-ai/k8sgpt/commit/632fc9a99fd0482dcff0768211c49bffb2e4032a))
* **deps:** update module github.com/aws/aws-sdk-go to v1.45.1 ([#624](https://github.com/k8sgpt-ai/k8sgpt/issues/624)) ([09984c2](https://github.com/k8sgpt-ai/k8sgpt/commit/09984c245de40fc7794f85a9535af4f8e5f5e776))
* **deps:** update module github.com/aws/aws-sdk-go to v1.45.2 ([#625](https://github.com/k8sgpt-ai/k8sgpt/issues/625)) ([b6498ef](https://github.com/k8sgpt-ai/k8sgpt/commit/b6498ef269919c61004dd860ebf08ed7f28810f7))
* **deps:** update module github.com/aws/aws-sdk-go to v1.45.3 ([#632](https://github.com/k8sgpt-ai/k8sgpt/issues/632)) ([5f73240](https://github.com/k8sgpt-ai/k8sgpt/commit/5f73240a0615e58a37e9eb00784628621bc1dfa1))
* **deps:** update module github.com/aws/aws-sdk-go to v1.45.6 ([#634](https://github.com/k8sgpt-ai/k8sgpt/issues/634)) ([3aabb48](https://github.com/k8sgpt-ai/k8sgpt/commit/3aabb4842d96ec14e61842847dc2feb3e3f31a0a))
* **deps:** update module github.com/aws/aws-sdk-go to v1.45.9 ([#640](https://github.com/k8sgpt-ai/k8sgpt/issues/640)) ([95787f2](https://github.com/k8sgpt-ai/k8sgpt/commit/95787f2854c4e4a971b2d687d97a5ceca30b9d5e))
* **deps:** update module github.com/sashabaranov/go-openai to v1.15.1 ([#622](https://github.com/k8sgpt-ai/k8sgpt/issues/622)) ([fc90dc8](https://github.com/k8sgpt-ai/k8sgpt/commit/fc90dc865b48fae99253b8bb6a8b1ae7047170b4))
* **deps:** update module golang.org/x/term to v0.12.0 ([#626](https://github.com/k8sgpt-ai/k8sgpt/issues/626)) ([44d17c5](https://github.com/k8sgpt-ai/k8sgpt/commit/44d17c51ff8ece92cd0c85f40d15caa97d990544))
* typos ([#629](https://github.com/k8sgpt-ai/k8sgpt/issues/629)) ([067c348](https://github.com/k8sgpt-ai/k8sgpt/commit/067c3483e6b379bd710c7f799de63bc1890b6c81))
* use-case while in cluster, connecting to an external ([#623](https://github.com/k8sgpt-ai/k8sgpt/issues/623)) ([1a7f45c](https://github.com/k8sgpt-ai/k8sgpt/commit/1a7f45cc55348d567148d01e61c7527e4d534f34))
### Other
* **deps:** bump github.com/cyphar/filepath-securejoin ([#644](https://github.com/k8sgpt-ai/k8sgpt/issues/644)) ([25890e6](https://github.com/k8sgpt-ai/k8sgpt/commit/25890e6e3807171e655fec0d2081cedad3ad6273))
* **deps:** update actions/checkout action to v4 ([#628](https://github.com/k8sgpt-ai/k8sgpt/issues/628)) ([e65d9a6](https://github.com/k8sgpt-ai/k8sgpt/commit/e65d9a650522120d602b2a62703aa2b39abfdea1))
* **deps:** update actions/checkout digest to f43a0e5 ([#612](https://github.com/k8sgpt-ai/k8sgpt/issues/612)) ([6f9f7b2](https://github.com/k8sgpt-ai/k8sgpt/commit/6f9f7b2b602605f3be7fd02bd521574e9c26fa78))
* **deps:** update docker/build-push-action action to v5 ([#643](https://github.com/k8sgpt-ai/k8sgpt/issues/643)) ([241f1bd](https://github.com/k8sgpt-ai/k8sgpt/commit/241f1bd6dfcb772711551aac42e48a2f59e64046))
* **deps:** update docker/login-action action to v3 ([#648](https://github.com/k8sgpt-ai/k8sgpt/issues/648)) ([b491c92](https://github.com/k8sgpt-ai/k8sgpt/commit/b491c9200e781284737dd74a9789dfc0c1e7b14a))
* **deps:** update docker/setup-buildx-action action to v3 ([#649](https://github.com/k8sgpt-ai/k8sgpt/issues/649)) ([598ef22](https://github.com/k8sgpt-ai/k8sgpt/commit/598ef22e570c1db678d583638c83e242f3b313d7))
* **deps:** update docker/setup-buildx-action digest to 885d146 ([#615](https://github.com/k8sgpt-ai/k8sgpt/issues/615)) ([2c81dad](https://github.com/k8sgpt-ai/k8sgpt/commit/2c81dadb4d4abcdc3608be768c1f3aae87e53a68))
* **deps:** update goreleaser/goreleaser-action action to v5 ([#641](https://github.com/k8sgpt-ai/k8sgpt/issues/641)) ([00d7a27](https://github.com/k8sgpt-ai/k8sgpt/commit/00d7a27ec1ea1bd49ab1879b8ffa0b9e7c0b6adf))
* **deps:** update goreleaser/goreleaser-action digest to 5fdedb9 ([#631](https://github.com/k8sgpt-ai/k8sgpt/issues/631)) ([5de3b64](https://github.com/k8sgpt-ai/k8sgpt/commit/5de3b640988783df5a04db368f79b9b9eefdb8bf))
* fixes a bug where filters do not deactive ([#621](https://github.com/k8sgpt-ai/k8sgpt/issues/621)) ([133850f](https://github.com/k8sgpt-ai/k8sgpt/commit/133850f984cc0bb41ec1e4521a32ab30558778f1))
* slice loop replace ([#627](https://github.com/k8sgpt-ai/k8sgpt/issues/627)) ([c24825b](https://github.com/k8sgpt-ai/k8sgpt/commit/c24825b81025c5cd79224a79b52d6c5efdc00511))
* updated protobuf libs ([#614](https://github.com/k8sgpt-ai/k8sgpt/issues/614)) ([5e17e66](https://github.com/k8sgpt-ai/k8sgpt/commit/5e17e666659c0eb057562def70d491daa995e5a2))
* updated schema for integrations support ([#616](https://github.com/k8sgpt-ai/k8sgpt/issues/616)) ([8f0a2fd](https://github.com/k8sgpt-ai/k8sgpt/commit/8f0a2fd41d6705da4d1a1d288f3b6ce19711f30d))
## [0.3.14](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.13...v0.3.14) (2023-08-25)
### Features
* configauditreport ([#609](https://github.com/k8sgpt-ai/k8sgpt/issues/609)) ([44d3613](https://github.com/k8sgpt-ai/k8sgpt/commit/44d3613c1f950837c6b112ddde0dc3e90f73dc1b))
### Bug Fixes
* **deps:** update kubernetes packages to v0.27.4 ([#565](https://github.com/k8sgpt-ai/k8sgpt/issues/565)) ([3cc7aa5](https://github.com/k8sgpt-ai/k8sgpt/commit/3cc7aa56d8efc6e78badf3be1cb3d5726074156e))
* **deps:** update module github.com/aquasecurity/trivy-operator to v0.15.1 ([#576](https://github.com/k8sgpt-ai/k8sgpt/issues/576)) ([c364074](https://github.com/k8sgpt-ai/k8sgpt/commit/c3640744c5cbf036321a14b90c1fdefa17c5321d))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.304 ([#558](https://github.com/k8sgpt-ai/k8sgpt/issues/558)) ([cf9069e](https://github.com/k8sgpt-ai/k8sgpt/commit/cf9069ef572fea9a947d7de5b0c0e44f34620a69))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.307 ([#574](https://github.com/k8sgpt-ai/k8sgpt/issues/574)) ([8ae91ec](https://github.com/k8sgpt-ai/k8sgpt/commit/8ae91ec744d1fead3b0aa570c904e9e3ad5ab5ef))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.308 ([#579](https://github.com/k8sgpt-ai/k8sgpt/issues/579)) ([7e8668a](https://github.com/k8sgpt-ai/k8sgpt/commit/7e8668a56bb25b7da3957cf4c05847d022825c10))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.309 ([#584](https://github.com/k8sgpt-ai/k8sgpt/issues/584)) ([227e1cd](https://github.com/k8sgpt-ai/k8sgpt/commit/227e1cd69f38654126750902a89408643bdb30fb))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.312 ([#586](https://github.com/k8sgpt-ai/k8sgpt/issues/586)) ([aafac93](https://github.com/k8sgpt-ai/k8sgpt/commit/aafac9345fbab16b1fe23ea76d6c1c362c44c080))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.313 ([#587](https://github.com/k8sgpt-ai/k8sgpt/issues/587)) ([f1479ba](https://github.com/k8sgpt-ai/k8sgpt/commit/f1479babbaaf6770d4a106d80f22b2ffb736cbad))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.315 ([#588](https://github.com/k8sgpt-ai/k8sgpt/issues/588)) ([fe29361](https://github.com/k8sgpt-ai/k8sgpt/commit/fe29361e335f3d186dc3d7651823e9bb03649652))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.317 ([#591](https://github.com/k8sgpt-ai/k8sgpt/issues/591)) ([9802e82](https://github.com/k8sgpt-ai/k8sgpt/commit/9802e82ff54bc55b670e25f75c69a29a985c21ae))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.327 ([#597](https://github.com/k8sgpt-ai/k8sgpt/issues/597)) ([aee83b7](https://github.com/k8sgpt-ai/k8sgpt/commit/aee83b74b20117f136876ec426318914aee8c4d1))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.329 ([#610](https://github.com/k8sgpt-ai/k8sgpt/issues/610)) ([0e5be89](https://github.com/k8sgpt-ai/k8sgpt/commit/0e5be89e5ccb70e9e9a44ad70f161c7b344d04f2))
* **deps:** update module github.com/mittwald/go-helm-client to v0.12.3 ([#582](https://github.com/k8sgpt-ai/k8sgpt/issues/582)) ([c2770f3](https://github.com/k8sgpt-ai/k8sgpt/commit/c2770f38a6f0d3248747927155505db505f5e960))
* **deps:** update module github.com/sashabaranov/go-openai to v1.14.1 ([#573](https://github.com/k8sgpt-ai/k8sgpt/issues/573)) ([b52424a](https://github.com/k8sgpt-ai/k8sgpt/commit/b52424a9b1a554739cb8e08e296045c181d4041c))
* **deps:** update module github.com/sashabaranov/go-openai to v1.14.2 ([#603](https://github.com/k8sgpt-ai/k8sgpt/issues/603)) ([81fcf8b](https://github.com/k8sgpt-ai/k8sgpt/commit/81fcf8b5d46387eca7128b877c0652fdf4ed999c))
* **deps:** update module go.uber.org/zap to v1.25.0 ([#589](https://github.com/k8sgpt-ai/k8sgpt/issues/589)) ([9672cea](https://github.com/k8sgpt-ai/k8sgpt/commit/9672cea228de976772f453e6a05ce05456741de8))
* **deps:** update module golang.org/x/term to v0.11.0 ([#593](https://github.com/k8sgpt-ai/k8sgpt/issues/593)) ([7f109cd](https://github.com/k8sgpt-ai/k8sgpt/commit/7f109cdcfac00a329a53121287e44c2567af6b4a))
* **deps:** update module google.golang.org/grpc to v1.57.0 ([#585](https://github.com/k8sgpt-ai/k8sgpt/issues/585)) ([59897f3](https://github.com/k8sgpt-ai/k8sgpt/commit/59897f330a037f1e5de0f958dd93b826e2ce481d))
* **deps:** update module helm.sh/helm/v3 to v3.12.3 ([#602](https://github.com/k8sgpt-ai/k8sgpt/issues/602)) ([7910c9a](https://github.com/k8sgpt-ai/k8sgpt/commit/7910c9aa2c40f3c1837cce179dd1fc91a9744946))
* optimize analyze service ([#461](https://github.com/k8sgpt-ai/k8sgpt/issues/461)) ([cc665ea](https://github.com/k8sgpt-ai/k8sgpt/commit/cc665ea4f3f279c30c7dd7996786e6bdce88acc8))
* use kubeconfig file when user specify it ([#605](https://github.com/k8sgpt-ai/k8sgpt/issues/605)) ([e3b21ec](https://github.com/k8sgpt-ai/k8sgpt/commit/e3b21ec5ecd5f823470c2c2f570ed89a2c071b5a)), closes [#604](https://github.com/k8sgpt-ai/k8sgpt/issues/604)
### Other
* **deps:** exclude retracted cohere-go versions ([#583](https://github.com/k8sgpt-ai/k8sgpt/issues/583)) ([f8a53a5](https://github.com/k8sgpt-ai/k8sgpt/commit/f8a53a5c035fd3e3598666d9792c4e1231f9838d))
* **deps:** update actions/setup-go digest to 93397be ([#600](https://github.com/k8sgpt-ai/k8sgpt/issues/600)) ([1a0ae1a](https://github.com/k8sgpt-ai/k8sgpt/commit/1a0ae1a086d328b1eaa70c412122427a6e8df2f5))
* **deps:** update google-github-actions/release-please-action digest to ca6063f ([#572](https://github.com/k8sgpt-ai/k8sgpt/issues/572)) ([fba1a8e](https://github.com/k8sgpt-ai/k8sgpt/commit/fba1a8ed8c7cc2f7b0aace246f8797ea6c27e455))
* **deps:** update goreleaser/goreleaser-action digest to 3fa32b8 ([#601](https://github.com/k8sgpt-ai/k8sgpt/issues/601)) ([610720a](https://github.com/k8sgpt-ai/k8sgpt/commit/610720a95c9d5eb49c77e7a929cd766a04e534a4))
* **deps:** update reviewdog/action-golangci-lint digest to 951dc8b ([#594](https://github.com/k8sgpt-ai/k8sgpt/issues/594)) ([9acaec0](https://github.com/k8sgpt-ai/k8sgpt/commit/9acaec00c4d084c4ec3e40e4a6a8b0136dcc4aa1))
* **deps:** update reviewdog/action-golangci-lint digest to f17c2e2 ([#598](https://github.com/k8sgpt-ai/k8sgpt/issues/598)) ([2251321](https://github.com/k8sgpt-ai/k8sgpt/commit/22513216960f06d572ec53480e290b1f4e5ff1d8))
* upgraded cohere backend ([#580](https://github.com/k8sgpt-ai/k8sgpt/issues/580)) ([43b0d70](https://github.com/k8sgpt-ai/k8sgpt/commit/43b0d707e7eac326594f5f6c7ab4c885772846d2))
## [0.3.13](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.12...v0.3.13) (2023-07-20)
### Other
* continue on absent service ([#569](https://github.com/k8sgpt-ai/k8sgpt/issues/569)) ([153d38d](https://github.com/k8sgpt-ai/k8sgpt/commit/153d38deb060cb84d606f8391e5700025ce02a9b))
## [0.3.12](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.11...v0.3.12) (2023-07-19)
### Features
* add Cohere backend ([#563](https://github.com/k8sgpt-ai/k8sgpt/issues/563)) ([781ecb7](https://github.com/k8sgpt-ai/k8sgpt/commit/781ecb7aad689e6709678c9690c112115e3cf6c7))
### Bug Fixes
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.300 ([#554](https://github.com/k8sgpt-ai/k8sgpt/issues/554)) ([dc46333](https://github.com/k8sgpt-ai/k8sgpt/commit/dc463334bccdf16106cff4e688a83bf0984d6e27))
* **deps:** update module github.com/mittwald/go-helm-client to v0.12.2 ([#562](https://github.com/k8sgpt-ai/k8sgpt/issues/562)) ([2e0db55](https://github.com/k8sgpt-ai/k8sgpt/commit/2e0db553f92b5ca691b5957b180be35131ab4e2f))
* **deps:** update module google.golang.org/grpc to v1.56.2 ([#546](https://github.com/k8sgpt-ai/k8sgpt/issues/546)) ([cc83fe1](https://github.com/k8sgpt-ai/k8sgpt/commit/cc83fe19bafc87647fa0293189f90c84d2dd8edb))
* **deps:** update module helm.sh/helm/v3 to v3.12.2 ([#555](https://github.com/k8sgpt-ai/k8sgpt/issues/555)) ([9eb96c4](https://github.com/k8sgpt-ai/k8sgpt/commit/9eb96c495cdb1247b664de625a036902b5e156ff))
### Other
* fixing edge cases with missing wh service ([#561](https://github.com/k8sgpt-ai/k8sgpt/issues/561)) ([c422215](https://github.com/k8sgpt-ai/k8sgpt/commit/c42221512bfdab7ac792963d459bf9f8dac3954c))
### Docs
* fix readme for anonymization ([#559](https://github.com/k8sgpt-ai/k8sgpt/issues/559)) ([70bec05](https://github.com/k8sgpt-ai/k8sgpt/commit/70bec050d854be6f559065278d6a583d8e0e333b))
## [0.3.11](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.10...v0.3.11) (2023-07-14)
### Features
* admission webhooks ([#553](https://github.com/k8sgpt-ai/k8sgpt/issues/553)) ([06e8532](https://github.com/k8sgpt-ai/k8sgpt/commit/06e8532f88616a988a4e41ed8cdac62cf0f243a5))
### Other
* **deps:** update docker/setup-buildx-action digest to 4c0219f ([#547](https://github.com/k8sgpt-ai/k8sgpt/issues/547)) ([1a3f299](https://github.com/k8sgpt-ai/k8sgpt/commit/1a3f2992108e857f8c8c07eff16599d00b50110e))
## [0.3.10](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.9...v0.3.10) (2023-07-12)
### Features
* add Validating/Mutating webhook analyzer ([#548](https://github.com/k8sgpt-ai/k8sgpt/issues/548)) ([750a10d](https://github.com/k8sgpt-ai/k8sgpt/commit/750a10d44c59bc90de5241d1128ee74fa38bf350))
### Bug Fixes
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.298 ([#545](https://github.com/k8sgpt-ai/k8sgpt/issues/545)) ([d1096dc](https://github.com/k8sgpt-ai/k8sgpt/commit/d1096dc31a692013f40980649e5cc2d402869ceb))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.299 ([#549](https://github.com/k8sgpt-ai/k8sgpt/issues/549)) ([ecd7790](https://github.com/k8sgpt-ai/k8sgpt/commit/ecd7790efe2ca88259451761202c90cb842ff04b))
* **deps:** update module github.com/sashabaranov/go-openai to v1.13.0 ([#399](https://github.com/k8sgpt-ai/k8sgpt/issues/399)) ([21df094](https://github.com/k8sgpt-ai/k8sgpt/commit/21df094bda31a14235fb2244e8cef74d3c92d919))
* **deps:** update module github.com/sashabaranov/go-openai to v1.14.0 ([#550](https://github.com/k8sgpt-ai/k8sgpt/issues/550)) ([9dcab94](https://github.com/k8sgpt-ai/k8sgpt/commit/9dcab945460e5972f895fa5302e3425750d635c7))
* **deps:** update module golang.org/x/term to v0.10.0 ([#542](https://github.com/k8sgpt-ai/k8sgpt/issues/542)) ([1276b3e](https://github.com/k8sgpt-ai/k8sgpt/commit/1276b3e89715b1cfb553e60d4f25592acef80a6f))
## [0.3.9](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.8...v0.3.9) (2023-07-04)
### Features
* details flag to list command ([#537](https://github.com/k8sgpt-ai/k8sgpt/issues/537)) ([2309b0d](https://github.com/k8sgpt-ai/k8sgpt/commit/2309b0dfe20e27b6afe283a6be21ad7a0652ac99))
* upgrading azure client impl ([#526](https://github.com/k8sgpt-ai/k8sgpt/issues/526)) ([367fe8f](https://github.com/k8sgpt-ai/k8sgpt/commit/367fe8f74c6a9e26f0d9c3b25a86093530fb85b2))
* upgrading the proto files to fix user issues ([#515](https://github.com/k8sgpt-ai/k8sgpt/issues/515)) ([c88fc88](https://github.com/k8sgpt-ai/k8sgpt/commit/c88fc889e4f6089e48f37d90e349d5c61ea0b952))
### Bug Fixes
* 'intergration' typos ([#508](https://github.com/k8sgpt-ai/k8sgpt/issues/508)) ([64b93c9](https://github.com/k8sgpt-ai/k8sgpt/commit/64b93c9116b6a7f82419f1c4fff98fa68b8c0aca))
* add --no-install for activate command ([#536](https://github.com/k8sgpt-ai/k8sgpt/issues/536)) ([1f5462c](https://github.com/k8sgpt-ai/k8sgpt/commit/1f5462c80bd04f63c2b55889c987634251635812))
* **deps:** update module buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go to v1.3.0-20230620082254-6f80f9533908.1 ([#516](https://github.com/k8sgpt-ai/k8sgpt/issues/516)) ([06e50d5](https://github.com/k8sgpt-ai/k8sgpt/commit/06e50d57db3aa2e5a68b093e2ba25c0e33dc6343))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.284 ([#501](https://github.com/k8sgpt-ai/k8sgpt/issues/501)) ([d87127a](https://github.com/k8sgpt-ai/k8sgpt/commit/d87127a309734847a56bf95c2e947e2270f94a88))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.286 ([#514](https://github.com/k8sgpt-ai/k8sgpt/issues/514)) ([b9cf522](https://github.com/k8sgpt-ai/k8sgpt/commit/b9cf5226853619655e98f2156bfd0b8513511bb3))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.288 ([#519](https://github.com/k8sgpt-ai/k8sgpt/issues/519)) ([57695b4](https://github.com/k8sgpt-ai/k8sgpt/commit/57695b44b6429319860a76e4e02016dafe3ed0b0))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.289 ([#524](https://github.com/k8sgpt-ai/k8sgpt/issues/524)) ([fafb695](https://github.com/k8sgpt-ai/k8sgpt/commit/fafb69544f4edda670bad6973332a20a7f0f055e))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.292 ([#530](https://github.com/k8sgpt-ai/k8sgpt/issues/530)) ([566f752](https://github.com/k8sgpt-ai/k8sgpt/commit/566f7525eef9f65dd2ab6a47bd0012bfb91e2a12))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.294 ([#535](https://github.com/k8sgpt-ai/k8sgpt/issues/535)) ([3067fa9](https://github.com/k8sgpt-ai/k8sgpt/commit/3067fa98f4a8990c9a930e53ad93f89cf35e0d62))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.295 ([#540](https://github.com/k8sgpt-ai/k8sgpt/issues/540)) ([767e4cb](https://github.com/k8sgpt-ai/k8sgpt/commit/767e4cbc4127e2017a3a9c4b182ccc833debc6a5))
* **deps:** update module github.com/prometheus/client_golang to v1.16.0 ([#507](https://github.com/k8sgpt-ai/k8sgpt/issues/507)) ([14e5691](https://github.com/k8sgpt-ai/k8sgpt/commit/14e5691190bf772c05477cbcb811ed71bec450a3))
* **deps:** update module google.golang.org/grpc to v1.56.0 ([#510](https://github.com/k8sgpt-ai/k8sgpt/issues/510)) ([f3e0b9b](https://github.com/k8sgpt-ai/k8sgpt/commit/f3e0b9b56d13397c79f57e76bdd6b741bb565fb4))
* **deps:** update module google.golang.org/grpc to v1.56.1 ([#520](https://github.com/k8sgpt-ai/k8sgpt/issues/520)) ([be52308](https://github.com/k8sgpt-ai/k8sgpt/commit/be52308c99f6aed73e2c20d260823795d45876f5))
* **deps:** update module helm.sh/helm/v3 to v3.12.1 ([#503](https://github.com/k8sgpt-ai/k8sgpt/issues/503)) ([0f03ddc](https://github.com/k8sgpt-ai/k8sgpt/commit/0f03ddcf0f5ec79bc6dbb74c654e0d8fac634a0a))
* displaying correct yaml config location on app start ([#521](https://github.com/k8sgpt-ai/k8sgpt/issues/521)) ([b7d4602](https://github.com/k8sgpt-ai/k8sgpt/commit/b7d4602cb8aaaa0c22a0a5941d8c6edad7c58db4))
* remove provider from default on delete ([#529](https://github.com/k8sgpt-ai/k8sgpt/issues/529)) ([5a983c4](https://github.com/k8sgpt-ai/k8sgpt/commit/5a983c4a0a511389e25cffe12999b903b85cd96d))
* typo in add command ([#539](https://github.com/k8sgpt-ai/k8sgpt/issues/539)) ([da750df](https://github.com/k8sgpt-ai/k8sgpt/commit/da750df16bde878f7619aa58ef5e7ef7d3173b2b))
### Other
* **deps:** update anchore/sbom-action action to v0.14.3 ([#517](https://github.com/k8sgpt-ai/k8sgpt/issues/517)) ([0521060](https://github.com/k8sgpt-ai/k8sgpt/commit/05210604109a6e892bb465df11038b8c24d68076))
* **deps:** update docker/build-push-action digest to 2eb1c19 ([#499](https://github.com/k8sgpt-ai/k8sgpt/issues/499)) ([9df75cc](https://github.com/k8sgpt-ai/k8sgpt/commit/9df75cc959f7ed23cae8e3761498ea6c56885788))
* **deps:** update docker/login-action digest to 465a078 ([#488](https://github.com/k8sgpt-ai/k8sgpt/issues/488)) ([c15a561](https://github.com/k8sgpt-ai/k8sgpt/commit/c15a561b635dc678bb8de15c6623914942475537))
* **deps:** update docker/setup-buildx-action digest to 16c0bc4 ([#532](https://github.com/k8sgpt-ai/k8sgpt/issues/532)) ([5662d59](https://github.com/k8sgpt-ai/k8sgpt/commit/5662d5932ff3beb8c1a31fc2088c5e703e90ec79))
* **deps:** update docker/setup-buildx-action digest to ecf9528 ([#498](https://github.com/k8sgpt-ai/k8sgpt/issues/498)) ([f4d7876](https://github.com/k8sgpt-ai/k8sgpt/commit/f4d78768388774f62d87acd89e71689535e538f7))
* **deps:** update google-github-actions/release-please-action digest to 8016a66 ([#523](https://github.com/k8sgpt-ai/k8sgpt/issues/523)) ([d56861d](https://github.com/k8sgpt-ai/k8sgpt/commit/d56861d4bad475da09992813fc256a0d99399eab))
* **deps:** update reviewdog/action-golangci-lint digest to 22adb9d ([#525](https://github.com/k8sgpt-ai/k8sgpt/issues/525)) ([3146754](https://github.com/k8sgpt-ai/k8sgpt/commit/314675477917063dcfb847880fb3186f8bdf32f6))
* **deps:** update reviewdog/action-golangci-lint digest to 994abff ([#513](https://github.com/k8sgpt-ai/k8sgpt/issues/513)) ([1819c3b](https://github.com/k8sgpt-ai/k8sgpt/commit/1819c3bf1512291cd637c115e8b82c9e0e8885a0))
* sorting out the dependency hell ([#518](https://github.com/k8sgpt-ai/k8sgpt/issues/518)) ([cd7807a](https://github.com/k8sgpt-ai/k8sgpt/commit/cd7807a48481f298422d9b1c8066b431fad3ae5a))
## [0.3.8](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.7...v0.3.8) (2023-06-15)
### Features
* fix for s3 cache from operator ([f6db6ce](https://github.com/k8sgpt-ai/k8sgpt/commit/f6db6ce86163dcb4b5ec4bd99b8a3842dd0c60bb))
### Bug Fixes
* **deps:** update kubernetes packages to v0.27.3 ([#504](https://github.com/k8sgpt-ai/k8sgpt/issues/504)) ([b1c6ec3](https://github.com/k8sgpt-ai/k8sgpt/commit/b1c6ec3c0919649bc551ec0047a1d9c2420f4264))
## [0.3.7](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.6...v0.3.7) (2023-06-13)
### Features
* add update to auth cmd ([#450](https://github.com/k8sgpt-ai/k8sgpt/issues/450)) ([01aeeb3](https://github.com/k8sgpt-ai/k8sgpt/commit/01aeeb35e2dab957d2909cd5cffc5a4a03e19664))
* support arbitrary uid for openshift environments ([#454](https://github.com/k8sgpt-ai/k8sgpt/issues/454)) ([92539ee](https://github.com/k8sgpt-ai/k8sgpt/commit/92539ee05d2d15e951742aaaf07f2defff3f79c5))
### Bug Fixes
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.274 ([#474](https://github.com/k8sgpt-ai/k8sgpt/issues/474)) ([1a81227](https://github.com/k8sgpt-ai/k8sgpt/commit/1a81227d6148be59b7b9ae4e9ae5e2d9a5b7a9ae))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.275 ([#478](https://github.com/k8sgpt-ai/k8sgpt/issues/478)) ([705b54f](https://github.com/k8sgpt-ai/k8sgpt/commit/705b54fcd308ef1fc0bc870b5a0a32baa30767df))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.276 ([#482](https://github.com/k8sgpt-ai/k8sgpt/issues/482)) ([3f0aea1](https://github.com/k8sgpt-ai/k8sgpt/commit/3f0aea131e1e62655a10f6a51bf6238316dd6598))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.277 ([#485](https://github.com/k8sgpt-ai/k8sgpt/issues/485)) ([e2d5c2d](https://github.com/k8sgpt-ai/k8sgpt/commit/e2d5c2dee00e3411fa10bcaa4ae134b5671f45ab))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.280 ([#490](https://github.com/k8sgpt-ai/k8sgpt/issues/490)) ([04b4f56](https://github.com/k8sgpt-ai/k8sgpt/commit/04b4f56a667febf77c21838a618a1cd4a7f1e371))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.281 ([#496](https://github.com/k8sgpt-ai/k8sgpt/issues/496)) ([a6342c9](https://github.com/k8sgpt-ai/k8sgpt/commit/a6342c92830451e3110a54bac80a1b693984fcc8))
* **deps:** update module golang.org/x/term to v0.9.0 ([#497](https://github.com/k8sgpt-ai/k8sgpt/issues/497)) ([98b852a](https://github.com/k8sgpt-ai/k8sgpt/commit/98b852aabe1ff62ac64e3c9e3e70173a8ff19749))
* use the `status` for pdb checking ([#477](https://github.com/k8sgpt-ai/k8sgpt/issues/477)) ([075066d](https://github.com/k8sgpt-ai/k8sgpt/commit/075066dd7c353c0afd36637f421229cba5a6e022)), closes [#476](https://github.com/k8sgpt-ai/k8sgpt/issues/476)
### Docs
* fix add localai command in readme.md ([#494](https://github.com/k8sgpt-ai/k8sgpt/issues/494)) ([40fbba7](https://github.com/k8sgpt-ai/k8sgpt/commit/40fbba7df1b2ce40c99262c901c7d2a26e9bbed0))
### Other
* customized prompt template for integration plugins ([#403](https://github.com/k8sgpt-ai/k8sgpt/issues/403)) ([c85203b](https://github.com/k8sgpt-ai/k8sgpt/commit/c85203bccde094c33ef83eb728aeed2608cbc136))
* **deps:** update actions/checkout digest to c85c95e ([#492](https://github.com/k8sgpt-ai/k8sgpt/issues/492)) ([1ae21e6](https://github.com/k8sgpt-ai/k8sgpt/commit/1ae21e6fd46b8490ea012fa8176d741af2e71e7e))
* **deps:** update docker/build-push-action digest to 44ea916 ([#491](https://github.com/k8sgpt-ai/k8sgpt/issues/491)) ([e556901](https://github.com/k8sgpt-ai/k8sgpt/commit/e556901b9d6205f75c819e1fbde51ba1f018e97d))
* **deps:** update docker/setup-buildx-action digest to 6a58db7 ([#489](https://github.com/k8sgpt-ai/k8sgpt/issues/489)) ([a23276d](https://github.com/k8sgpt-ai/k8sgpt/commit/a23276d3ff740abc6d3b36a4c793d90387ecee08))
* **deps:** update goreleaser/goreleaser-action digest to 336e299 ([#495](https://github.com/k8sgpt-ai/k8sgpt/issues/495)) ([ad2a5fd](https://github.com/k8sgpt-ai/k8sgpt/commit/ad2a5fd5fce78bf1bda4a48ad4a21598abadcdf4))
## [0.3.6](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.5...v0.3.6) (2023-05-31)
### Features
* get official field doc ([#457](https://github.com/k8sgpt-ai/k8sgpt/issues/457)) ([f9621af](https://github.com/k8sgpt-ai/k8sgpt/commit/f9621af7e480f490710020b931cbb08fb9824740))
### Bug Fixes
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.270 ([#465](https://github.com/k8sgpt-ai/k8sgpt/issues/465)) ([5cf4fc5](https://github.com/k8sgpt-ai/k8sgpt/commit/5cf4fc52da4542a8bae98764d2fa7e337d95e5bd))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.271 ([#469](https://github.com/k8sgpt-ai/k8sgpt/issues/469)) ([1459dd4](https://github.com/k8sgpt-ai/k8sgpt/commit/1459dd4b8eca937e95ebe9b727311dc8b023e304))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.272 ([#473](https://github.com/k8sgpt-ai/k8sgpt/issues/473)) ([5233627](https://github.com/k8sgpt-ai/k8sgpt/commit/523362765f4c064c02798bb9e6f31e2bcc856e5f))
* **deps:** update module github.com/spf13/viper to v1.16.0 ([#472](https://github.com/k8sgpt-ai/k8sgpt/issues/472)) ([6052a5b](https://github.com/k8sgpt-ai/k8sgpt/commit/6052a5b4d77902e1882e3121b678671c89b57af8))
* **deps:** update module github.com/stretchr/testify to v1.8.4 ([#471](https://github.com/k8sgpt-ai/k8sgpt/issues/471)) ([42437f7](https://github.com/k8sgpt-ai/k8sgpt/commit/42437f77d1e0735a8f38a62ddbefb4d1f4e61c0e))
* name of sa reference in deployment ([#468](https://github.com/k8sgpt-ai/k8sgpt/issues/468)) ([cd049c9](https://github.com/k8sgpt-ai/k8sgpt/commit/cd049c9b4b188f702608d989fb32ae62f333dac5))
* typo ([#463](https://github.com/k8sgpt-ai/k8sgpt/issues/463)) ([1b86a6f](https://github.com/k8sgpt-ai/k8sgpt/commit/1b86a6fc89f90d29fdf2fab87a517f0da225ec96))
### Other
* **deps:** update google-github-actions/release-please-action digest to 51ee8ae ([#464](https://github.com/k8sgpt-ai/k8sgpt/issues/464)) ([86ebc23](https://github.com/k8sgpt-ai/k8sgpt/commit/86ebc23de762583b5904605f5651bbc83760aa95))
## [0.3.5](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.4...v0.3.5) (2023-05-25)
### Features
* add configuration api route ([#459](https://github.com/k8sgpt-ai/k8sgpt/issues/459)) ([fa4a075](https://github.com/k8sgpt-ai/k8sgpt/commit/fa4a0757b83f8ec00df951d49316f10961daa0e0))
### Bug Fixes
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.267 ([#451](https://github.com/k8sgpt-ai/k8sgpt/issues/451)) ([49e120c](https://github.com/k8sgpt-ai/k8sgpt/commit/49e120c28e8b5ce5a8f7255ebc0f1b1b5c423f95))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.269 ([#458](https://github.com/k8sgpt-ai/k8sgpt/issues/458)) ([2994c1c](https://github.com/k8sgpt-ai/k8sgpt/commit/2994c1c5a77ce6ebe6e59d6edc9647c02f06f261))
* updated list.go to handle k8sgpt cache list crashing issue ([#455](https://github.com/k8sgpt-ai/k8sgpt/issues/455)) ([6eac58d](https://github.com/k8sgpt-ai/k8sgpt/commit/6eac58d4b03169356d3f06674ef206472e149fde))
## [0.3.4](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.3...v0.3.4) (2023-05-22)
### Bug Fixes
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.266 ([#446](https://github.com/k8sgpt-ai/k8sgpt/issues/446)) ([edda743](https://github.com/k8sgpt-ai/k8sgpt/commit/edda743fa2bf4b5ae2551c981447a5912a459bb4))
* **deps:** update module github.com/stretchr/testify to v1.8.3 ([#442](https://github.com/k8sgpt-ai/k8sgpt/issues/442)) ([fe450eb](https://github.com/k8sgpt-ai/k8sgpt/commit/fe450eb69da0645328e60e2d7b0852ffdb996dee))
### Other
* add more filter releavent UT in analysis_test.go ([#435](https://github.com/k8sgpt-ai/k8sgpt/issues/435)) ([36995fd](https://github.com/k8sgpt-ai/k8sgpt/commit/36995fd4ed41c8c83ebc308f8ec2a4bfe530f7dc))
## [0.3.3](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.2...v0.3.3) (2023-05-20)
### Features
* caching ([#439](https://github.com/k8sgpt-ai/k8sgpt/issues/439)) ([948dae5](https://github.com/k8sgpt-ai/k8sgpt/commit/948dae5e288ec3bb0165eb3ce32171b12003f9c7))
* rework auth commands ([#438](https://github.com/k8sgpt-ai/k8sgpt/issues/438)) ([c659a87](https://github.com/k8sgpt-ai/k8sgpt/commit/c659a875fc296849a3703bfd7f0c4796f44bdf5a))
### Bug Fixes
* append coreAnalyzer if active_filter is empty and integration is added ([#441](https://github.com/k8sgpt-ai/k8sgpt/issues/441)) ([b7dc384](https://github.com/k8sgpt-ai/k8sgpt/commit/b7dc3845476759bedb3a55f77c8779e4a9f460dd))
* **deps:** update kubernetes packages to v0.27.2 ([#436](https://github.com/k8sgpt-ai/k8sgpt/issues/436)) ([d13b913](https://github.com/k8sgpt-ai/k8sgpt/commit/d13b91301cab5e05349b68716cd506fa1705f36f))
* **deps:** update module github.com/aws/aws-sdk-go to v1.44.265 ([#445](https://github.com/k8sgpt-ai/k8sgpt/issues/445)) ([c588e96](https://github.com/k8sgpt-ai/k8sgpt/commit/c588e963de3f238f07200d6cf09fe6f9781484f5))
* docker version ([#444](https://github.com/k8sgpt-ai/k8sgpt/issues/444)) ([1f767eb](https://github.com/k8sgpt-ai/k8sgpt/commit/1f767ebd2e31e61decab36218b1b85f2b3b6207d))
* use coreAnalyzer if there are no filters selected and no active_filters ([#432](https://github.com/k8sgpt-ai/k8sgpt/issues/432)) ([f0d3f36](https://github.com/k8sgpt-ai/k8sgpt/commit/f0d3f36f6d56bd76248590c0b841dffb7769a2ee))
## [0.3.2](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.1...v0.3.2) (2023-05-16)
### Features
* added the ability to set a user default AI provider ([#427](https://github.com/k8sgpt-ai/k8sgpt/issues/427)) ([cbe2fb4](https://github.com/k8sgpt-ai/k8sgpt/commit/cbe2fb4a4c160a0a24b3fb4602cae8e5eebd6aa0))
### Bug Fixes
* improve default_prompt ([#406](https://github.com/k8sgpt-ai/k8sgpt/issues/406)) ([06542b4](https://github.com/k8sgpt-ai/k8sgpt/commit/06542b4bf1aec193f11a40526a1b60be01972e66))
* missing validation for backend option in remove command ([#429](https://github.com/k8sgpt-ai/k8sgpt/issues/429)) ([af826d5](https://github.com/k8sgpt-ai/k8sgpt/commit/af826d500fef0469b958250161b0827aa4ab2933))
### Other
* **deps:** bump github.com/docker/distribution ([#428](https://github.com/k8sgpt-ai/k8sgpt/issues/428)) ([3099909](https://github.com/k8sgpt-ai/k8sgpt/commit/30999091136c64173e5c15b789036c85f8b855f3))
* **deps:** update actions/setup-go digest to fac708d ([#422](https://github.com/k8sgpt-ai/k8sgpt/issues/422)) ([097c791](https://github.com/k8sgpt-ai/k8sgpt/commit/097c7912b0572d0461f08af12e9f21c6618c13e4))
* **deps:** update reviewdog/action-golangci-lint digest to 79d32f1 ([#425](https://github.com/k8sgpt-ai/k8sgpt/issues/425)) ([032576c](https://github.com/k8sgpt-ai/k8sgpt/commit/032576c728751522fe6cd8f255366cc3d5c0f23c))
## [0.3.1](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.0...v0.3.1) (2023-05-15)
### Features
* add error message if analyze request fail ([#393](https://github.com/k8sgpt-ai/k8sgpt/issues/393)) ([639aa12](https://github.com/k8sgpt-ai/k8sgpt/commit/639aa12931b3fc9f99a4b34f33f583b9f427f40c))
* add remove command to remove a backend AI provider ([#395](https://github.com/k8sgpt-ai/k8sgpt/issues/395)) ([c5b72ee](https://github.com/k8sgpt-ai/k8sgpt/commit/c5b72eee165a29d4ed59b0ec2f19e9f58267840d))
* filters api ([#407](https://github.com/k8sgpt-ai/k8sgpt/issues/407)) ([e5e613a](https://github.com/k8sgpt-ai/k8sgpt/commit/e5e613acee0a99f108dd90affc55a18dee42ad9c))
* use correct port to metrics ([#390](https://github.com/k8sgpt-ai/k8sgpt/issues/390)) ([5d4e591](https://github.com/k8sgpt-ai/k8sgpt/commit/5d4e591f11e555cac851205fff158ef22f702c33))
### Bug Fixes
* clusterole name ([#392](https://github.com/k8sgpt-ai/k8sgpt/issues/392)) ([123b8a6](https://github.com/k8sgpt-ai/k8sgpt/commit/123b8a66eed1af41b7bd4e558ba4f0f8ef947e98))
* **deps:** update module buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go to v1.3.0-20230515081240-6b5b845c638e.1 ([#397](https://github.com/k8sgpt-ai/k8sgpt/issues/397)) ([a1f98ad](https://github.com/k8sgpt-ai/k8sgpt/commit/a1f98ad78ecd9a84d26e7a59c340e5410524e561))
* **deps:** update module buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go to v1.28.1-20230510140658-54288a50e81c.4 ([#398](https://github.com/k8sgpt-ai/k8sgpt/issues/398)) ([50916f2](https://github.com/k8sgpt-ai/k8sgpt/commit/50916f2c93fb19b935f835eda1ba78b7c0465fe6))
* **deps:** update module google.golang.org/grpc to v1.55.0 ([#389](https://github.com/k8sgpt-ai/k8sgpt/issues/389)) ([8cfb717](https://github.com/k8sgpt-ai/k8sgpt/commit/8cfb717dc15a6af489a16282d38d3495518bc919))
* **deps:** update module helm.sh/helm/v3 to v3.12.0 ([#396](https://github.com/k8sgpt-ai/k8sgpt/issues/396)) ([c1410d1](https://github.com/k8sgpt-ai/k8sgpt/commit/c1410d169945341e9a635e12c2adcc87a08f8a09))
* update engine's cmd flag to match the new cli layout ([#400](https://github.com/k8sgpt-ai/k8sgpt/issues/400)) ([aafe669](https://github.com/k8sgpt-ai/k8sgpt/commit/aafe669739aa8c38611d13deb08706096c7893e0))
### Other
* gofmt fix and enable in CI ([#414](https://github.com/k8sgpt-ai/k8sgpt/issues/414)) ([e66de8c](https://github.com/k8sgpt-ai/k8sgpt/commit/e66de8c4cea1213cda1db609f07cbb0c8f6591c3))
* make go-lint happy ([#405](https://github.com/k8sgpt-ai/k8sgpt/issues/405)) ([ed73485](https://github.com/k8sgpt-ai/k8sgpt/commit/ed73485d92af0329915633d51c7eccdbce9b37cc))
## [0.3.0](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.2.9...v0.3.0) (2023-05-09)
### ⚠ BREAKING CHANGES
* migrate api to grpc ([#386](https://github.com/k8sgpt-ai/k8sgpt/issues/386))
### Features
* add auth commands ([#369](https://github.com/k8sgpt-ai/k8sgpt/issues/369)) ([00aaae8](https://github.com/k8sgpt-ai/k8sgpt/commit/00aaae86d88812bd6b6be290ba440ea583ccc01c))
* migrate api to grpc ([#386](https://github.com/k8sgpt-ai/k8sgpt/issues/386)) ([9998e76](https://github.com/k8sgpt-ai/k8sgpt/commit/9998e7620d2803b82b241482649449507040add3))
### Bug Fixes
* **deps:** update module github.com/sashabaranov/go-openai to v1.9.3 ([#378](https://github.com/k8sgpt-ai/k8sgpt/issues/378)) ([045a063](https://github.com/k8sgpt-ai/k8sgpt/commit/045a06350bf41d4177e67316978af8fcf02ff19a))
* **deps:** update module golang.org/x/term to v0.8.0 ([#382](https://github.com/k8sgpt-ai/k8sgpt/issues/382)) ([65fff11](https://github.com/k8sgpt-ai/k8sgpt/commit/65fff11e585f8074fb77124b25339a09da313970))
### Docs
* update README ([#383](https://github.com/k8sgpt-ai/k8sgpt/issues/383)) ([d6bcb96](https://github.com/k8sgpt-ai/k8sgpt/commit/d6bcb96105a549eb772b790704c7ec27e374eafd))
### Other
* **deps:** update anchore/sbom-action action to v0.14.2 ([#387](https://github.com/k8sgpt-ai/k8sgpt/issues/387)) ([9192b26](https://github.com/k8sgpt-ai/k8sgpt/commit/9192b26fab2ce09c8a480256a15723ae788612c3))
* fix the logo URL ([#384](https://github.com/k8sgpt-ai/k8sgpt/issues/384)) ([b6b0612](https://github.com/k8sgpt-ai/k8sgpt/commit/b6b06123db8914cae09dfa96edd92aff83823bdf))
## [0.2.9](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.2.8...v0.2.9) (2023-05-03)
### Features
* add additionalLabels to Service Monitor ([#366](https://github.com/k8sgpt-ai/k8sgpt/issues/366)) ([a89a5cf](https://github.com/k8sgpt-ai/k8sgpt/commit/a89a5cfa2efd365cfc1c501c7055a315949c5f97))
* add azure openai provider ([#309](https://github.com/k8sgpt-ai/k8sgpt/issues/309)) ([d8357ce](https://github.com/k8sgpt-ai/k8sgpt/commit/d8357ceb949e04d9dd21276a1d1dfcb60010c37a))
* add helm chart ([#318](https://github.com/k8sgpt-ai/k8sgpt/issues/318)) ([5af8178](https://github.com/k8sgpt-ai/k8sgpt/commit/5af817880278771cf0b25d0936b90a45c089218c))
* improving readme ([7471b76](https://github.com/k8sgpt-ai/k8sgpt/commit/7471b7679469bc7416ee35061a13e8442bfc532c))
* rework output format ([#368](https://github.com/k8sgpt-ai/k8sgpt/issues/368)) ([8b49f70](https://github.com/k8sgpt-ai/k8sgpt/commit/8b49f708f364569994801757cf72982291c0de82))
### Bug Fixes
* **deps:** update module github.com/aquasecurity/trivy-operator to v0.13.2 ([#353](https://github.com/k8sgpt-ai/k8sgpt/issues/353)) ([363294c](https://github.com/k8sgpt-ai/k8sgpt/commit/363294c310ca1a6666d4d2dcba7e232387f0d41f))
* **deps:** update module github.com/prometheus/client_golang to v1.15.1 ([#374](https://github.com/k8sgpt-ai/k8sgpt/issues/374)) ([799869b](https://github.com/k8sgpt-ai/k8sgpt/commit/799869bcef76be0d5b2169646de106bd3b441c50))
* **deps:** update module github.com/sashabaranov/go-openai to v1.9.1 ([#363](https://github.com/k8sgpt-ai/k8sgpt/issues/363)) ([766a15b](https://github.com/k8sgpt-ai/k8sgpt/commit/766a15b25a2669b101c717013dae0ea4f5d0daa3))
* **deps:** update module github.com/sashabaranov/go-openai to v1.9.2 ([#375](https://github.com/k8sgpt-ai/k8sgpt/issues/375)) ([8b82d59](https://github.com/k8sgpt-ai/k8sgpt/commit/8b82d59bd7a6837a543467f9d76c66e70f267b85))
* update CONTRIBUTING.md Slack URL ([#373](https://github.com/k8sgpt-ai/k8sgpt/issues/373)) ([e0200e7](https://github.com/k8sgpt-ai/k8sgpt/commit/e0200e7fa05597b52c9f25fc2e9b744aea764b9a))
### Docs
* remove issue templates to use org wide ones ([#352](https://github.com/k8sgpt-ai/k8sgpt/issues/352)) ([3051b1c](https://github.com/k8sgpt-ai/k8sgpt/commit/3051b1ca343d739e48ff05c96468040adefc929a))
* update README.md ([#356](https://github.com/k8sgpt-ai/k8sgpt/issues/356)) ([4e146fb](https://github.com/k8sgpt-ai/k8sgpt/commit/4e146fb152b79a43f3163a94389022b4325126d4))
### Other
* added changing banners ([#367](https://github.com/k8sgpt-ai/k8sgpt/issues/367)) ([4f6e833](https://github.com/k8sgpt-ai/k8sgpt/commit/4f6e833d3427adf82438186f52eee40293c50cd0))
* **deps:** update golang docker tag to v1.20.4 ([#370](https://github.com/k8sgpt-ai/k8sgpt/issues/370)) ([9faa694](https://github.com/k8sgpt-ai/k8sgpt/commit/9faa69422dab812ec062c10a8fffb73160a3cd21))
* **deps:** update reviewdog/action-golangci-lint digest to f5d8591 ([#362](https://github.com/k8sgpt-ai/k8sgpt/issues/362)) ([b8a5f3b](https://github.com/k8sgpt-ai/k8sgpt/commit/b8a5f3bab85e28f83259739958fe22ca0ada211a))
* updated logo ([#365](https://github.com/k8sgpt-ai/k8sgpt/issues/365)) ([6431be7](https://github.com/k8sgpt-ai/k8sgpt/commit/6431be7771bd7c34d6beac14470bc6918b1793c4))
## [0.2.8](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.2.7...v0.2.8) (2023-04-27)

View File

@@ -17,7 +17,7 @@ We're happy that you want to contribute to this project. Please read the section
- We are also happy to help you find something to work on. Just reach out to us.
**Getting in touch with the community**
* Join our [#k8sgpt slack channel](https://join.slack.com/t/k8sgpt/shared_invite/zt-1rwe5fpzq-VNtJK8DmYbbm~iWL1H34nw)
* Join our [#k8sgpt slack channel](https://slack.cloud-native.io/channels/k8sgpt)
* Introduce yourself on the slack channel or open an issue to let us know that you are interested in contributing
**Discuss issues**
@@ -30,7 +30,7 @@ We're happy that you want to contribute to this project. Please read the section
- Assign yourself to the issue, if you are working on it (if you are not a member of the organization, please leave a comment on the issue)
- Make your changes
- Keep pull requests small and focused, if you have multiple changes, please open multiple PRs
- Create a pull request back to the upstream repository and follow the [pull request template](.github/pull_request_template.md) guidelines.
- Create a pull request back to the upstream repository and follow follow the [pull request template](.github/pull_request_template.md) guidelines.
- Wait for a review and address any comments
**Opening PRs**

View File

@@ -10,8 +10,6 @@ ROOT_PACKAGE=github.com/k8sgpt-ai/k8sgpt
SHELL := /bin/bash
DIRS=$(shell ls)
GO=go
GOOS ?= $(shell go env GOOS)
GOARCH ?= $(shell go env GOARCH)
.DEFAULT_GOAL := help
@@ -54,7 +52,7 @@ all: tidy add-copyright lint cover build
build:
@echo "$(shell go version)"
@echo "===========> Building binary $(BUILDAPP) *[Git Info]: $(VERSION)-$(GIT_COMMIT)"
@export CGO_ENABLED=0 && go build -o $(BUILDAPP) -ldflags "-s -w -X main.version=dev -X main.commit=$$(git rev-parse --short HEAD) -X main.date=$$(date +%FT%TZ)" $(BUILDFILE)
@export CGO_ENABLED=0 && go build -o $(BUILDAPP) -ldflags '-s -w' $(BUILDFILE)
## tidy: tidy go.mod
.PHONY: tidy
@@ -63,21 +61,17 @@ tidy:
## deploy: Deploy k8sgpt
.PHONY: deploy
deploy: helm
deploy:
@echo "===========> Deploying k8sgpt"
$(HELM) install k8sgpt charts/k8sgpt -n k8sgpt --create-namespace
## update: Update k8sgpt
.PHONY: update
update: helm
@echo "===========> Updating k8sgpt"
$(HELM) upgrade k8sgpt charts/k8sgpt -n k8sgpt
@$(call funcsecret)
## undeploy: Undeploy k8sgpt
.PHONY: undeploy
undeploy: helm
undeploy:
@echo "===========> Undeploying k8sgpt"
$(HELM) uninstall k8sgpt -n k8sgpt
kubectl delete secret ai-backend-secret --namespace=k8sgpt
kubectl delete -f container/manifests
kubectl delete ns k8sgpt
## docker-build: Build docker image
.PHONY: docker-build
@@ -85,6 +79,7 @@ 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
## fmt: Run go fmt against code.
.PHONY: fmt
fmt:
@@ -142,16 +137,11 @@ copyright.add: tools.verify.addlicense
# @addlicense -y $(shell date +"%Y") -v -c "K8sgpt AI." -f $(LICENSE_TEMPLATE) $(CODE_DIRS)
@echo "===========> End the copyright is added..."
# =====
# Tools
HELM_VERSION ?= v3.11.3
helm:
if ! test -f $(OUTPUT_DIR)/helm-$(GOOS)-$(GOARCH); then \
curl -L https://get.helm.sh/helm-$(HELM_VERSION)-$(GOOS)-$(GOARCH).tar.gz | tar xz; \
mv $(GOOS)-$(GOARCH)/helm $(OUTPUT_DIR)/helm-$(GOOS)-$(GOARCH); \
chmod +x $(OUTPUT_DIR)/helm-$(GOOS)-$(GOARCH); \
rm -rf ./$(GOOS)-$(GOARCH)/; \
fi
HELM=$(OUTPUT_DIR)/helm-$(GOOS)-$(GOARCH)
define funcsecret
ifndef SECRET
$(error SECRET environment variable is not set)
endif
kubectl create ns k8sgpt || true
kubectl create secret generic ai-backend-secret --from-literal=secret-key=$(SECRET) --namespace=k8sgpt || true
kubectl apply -f container/manifests
endef

367
README.md
View File

@@ -1,31 +1,21 @@
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./images/banner-white.png" width="600px;">
<img alt="Text changing depending on mode. Light: 'So light!' Dark: 'So dark!'" src="./images/banner-black.png" width="600px;">
</picture>
<br/>
<img src="./images/banner-white.png" width="600px;" />
![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/k8sgpt-ai/k8sgpt)
![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/k8sgpt-ai/k8sgpt/release.yaml)
![GitHub release (latest by date)](https://img.shields.io/github/v/release/k8sgpt-ai/k8sgpt)
[![OpenSSF Best Practices](https://bestpractices.coreinfrastructure.org/projects/7272/badge)](https://bestpractices.coreinfrastructure.org/projects/7272)
[![Link to documentation](https://img.shields.io/static/v1?label=%F0%9F%93%96&message=Documentation&color=blue)](https://docs.k8sgpt.ai/)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fk8sgpt-ai%2Fk8sgpt.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fk8sgpt-ai%2Fk8sgpt?ref=badge_shield)
[![codecov](https://codecov.io/github/k8sgpt-ai/k8sgpt/graph/badge.svg?token=ZLR7NG8URE)](https://codecov.io/github/k8sgpt-ai/k8sgpt)
`k8sgpt` is a tool for scanning your Kubernetes clusters, diagnosing, and triaging issues in simple English.
It has SRE experience codified into its analyzers and helps to pull out the most relevant information to enrich it with AI.
_Out of the box integration with OpenAI, Azure, Cohere, Amazon Bedrock and local models._
<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&#0032;gives&#0032;Kubernetes&#0032;Superpowers&#0032;to&#0032;everyone | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
<img src="images/demo4.gif" width=650px; />
# CLI Installation
# Installation
### Linux/Mac via brew
## Linux/Mac via brew
```
brew tap k8sgpt-ai/k8sgpt
@@ -38,7 +28,7 @@ brew install k8sgpt
**32 bit:**
<!---x-release-please-start-version-->
```
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.26/k8sgpt_386.rpm
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.2.8/k8sgpt_386.rpm
sudo rpm -ivh k8sgpt_386.rpm
```
<!---x-release-please-end-->
@@ -47,7 +37,7 @@ brew install k8sgpt
<!---x-release-please-start-version-->
```
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.26/k8sgpt_amd64.rpm
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.2.8/k8sgpt_amd64.rpm
sudo rpm -ivh -i k8sgpt_amd64.rpm
```
<!---x-release-please-end-->
@@ -59,7 +49,7 @@ brew install k8sgpt
**32 bit:**
<!---x-release-please-start-version-->
```
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.26/k8sgpt_386.deb
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.2.8/k8sgpt_386.deb
sudo dpkg -i k8sgpt_386.deb
```
<!---x-release-please-end-->
@@ -67,7 +57,7 @@ brew install k8sgpt
<!---x-release-please-start-version-->
```
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.26/k8sgpt_amd64.deb
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.2.8/k8sgpt_amd64.deb
sudo dpkg -i k8sgpt_amd64.deb
```
<!---x-release-please-end-->
@@ -80,14 +70,14 @@ brew install k8sgpt
**32 bit:**
<!---x-release-please-start-version-->
```
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.26/k8sgpt_386.apk
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.2.8/k8sgpt_386.apk
apk add k8sgpt_386.apk
```
<!---x-release-please-end-->
**64 bit:**
<!---x-release-please-start-version-->
```
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.26/k8sgpt_amd64.apk
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.2.8/k8sgpt_amd64.apk
apk add k8sgpt_amd64.apk
```
<!---x-release-please-end-->x
@@ -110,29 +100,30 @@ If you install gcc as suggested, the problem will persist. Therefore, you need t
</details>
### Windows
## Windows
* Download the latest Windows binaries of **k8sgpt** from the [Release](https://github.com/k8sgpt-ai/k8sgpt/releases)
tab based on your system architecture.
* Extract the downloaded package to your desired location. Configure the system *path* variable with the binary location
## Operator Installation
To install within a Kubernetes cluster please use our `k8sgpt-operator` with installation instructions available [here](https://github.com/k8sgpt-ai/k8sgpt-operator)
## Verify installation
_This mode of operation is ideal for continuous monitoring of your cluster and can integrate with your existing monitoring such as Prometheus and Alertmanager._
* Run `k8sgpt version`
<hr>
## Quick Start
* Currently, the default AI provider is OpenAI, you will need to generate an API key from [OpenAI](https://openai.com)
* Currently the default AI provider is OpenAI, you will need to generate an API key from [OpenAI](https://openai.com)
* You can do this by running `k8sgpt generate` to open a browser link to generate it
* Run `k8sgpt auth add` to set it in k8sgpt.
* Run `k8sgpt auth` to set it in k8sgpt.
* You can provide the password directly using the `--password` flag.
* Run `k8sgpt filters` to manage the active filters used by the analyzer. By default, all filters are executed during analysis.
* Run `k8sgpt analyze` to run a scan.
* And use `k8sgpt analyze --explain` to get a more detailed explanation of the issues.
* You also run `k8sgpt analyze --with-doc` (with or without the explain flag) to get the official documentation from Kubernetes.
<img src="images/demo4.gif" width=650px; />
## Analyzers
@@ -153,27 +144,77 @@ you will be able to write your own analyzers.
- [x] deploymentAnalyzer
- [x] cronJobAnalyzer
- [x] nodeAnalyzer
- [x] mutatingWebhookAnalyzer
- [x] validatingWebhookAnalyzer
#### Optional
- [x] hpaAnalyzer
- [x] pdbAnalyzer
- [x] networkPolicyAnalyzer
- [x] gatewayClass
- [x] gateway
- [x] httproute
## Examples
## Usage
```
Kubernetes debugging powered by AI
Usage:
k8sgpt [command]
Available Commands:
analyze This command will find problems within your Kubernetes cluster
auth Authenticate with your chosen backend
completion Generate the autocompletion script for the specified shell
filters Manage filters for analyzing Kubernetes resources
generate Generate Key for your chosen backend (opens browser)
help Help about any command
integration Intergrate another tool into K8sGPT
serve Runs k8sgpt as a server
version Print the version number of k8sgpt
Flags:
--config string config file (default is $HOME/.k8sgpt.yaml)
-h, --help help for k8sgpt
--kubeconfig string Path to a kubeconfig. Only required if out-of-cluster. (default "$HOME/.kube/config")
--kubecontext string Kubernetes context to use. Only required if out-of-cluster.
Use "k8sgpt [command] --help" for more information about a command.
```
_Manage filters_
_List filters_
```
k8sgpt filters list
```
_Add default filters_
```
k8sgpt filters add [filter(s)]
```
### Examples :
- Simple filter : `k8sgpt filters add Service`
- Multiple filters : `k8sgpt filters add Ingress,Pod`
_Add default filters_
```
k8sgpt filters remove [filter(s)]
```
### Examples :
- Simple filter : `k8sgpt filters remove Service`
- Multiple filters : `k8sgpt filters remove Ingress,Pod`
_Run a scan with the default analyzers_
```
k8sgpt generate
k8sgpt auth add
k8sgpt auth
k8sgpt analyze --explain
k8sgpt analyze --explain --with-doc
```
_Filter on resource_
@@ -199,60 +240,41 @@ _Anonymize during explain_
k8sgpt analyze --explain --filter=Service --output=json --anonymize
```
### How does anonymization work?
With this option, the data is anonymized before being sent to the AI Backend. During the analysis execution, `k8sgpt` retrieves sensitive data (Kubernetes object names, labels, etc.). This data is masked when sent to the AI backend and replaced by a key that can be used to de-anonymize the data when the solution is returned to the user.
<details>
<summary> Using filters </summary>
_List filters_
```
k8sgpt filters list
1. Error reported during analysis:
```bash
Error: HorizontalPodAutoscaler uses StatefulSet/fake-deployment as ScaleTargetRef which does not exist.
```
_Add default filters_
```
k8sgpt filters add [filter(s)]
2. Payload sent to the AI backend:
```bash
Error: HorizontalPodAutoscaler uses StatefulSet/tGLcCRcHa1Ce5Rs as ScaleTargetRef which does not exist.
```
### Examples :
- Simple filter : `k8sgpt filters add Service`
- Multiple filters : `k8sgpt filters add Ingress,Pod`
_Remove default filters_
```
k8sgpt filters remove [filter(s)]
3. Payload returned by the AI:
```bash
The Kubernetes system is trying to scale a StatefulSet named tGLcCRcHa1Ce5Rs using the HorizontalPodAutoscaler, but it cannot find the StatefulSet. The solution is to verify that the StatefulSet name is spelled correctly and exists in the same namespace as the HorizontalPodAutoscaler.
```
### Examples :
4. Payload returned to the user:
```bash
The Kubernetes system is trying to scale a StatefulSet named fake-deployment using the HorizontalPodAutoscaler, but it cannot find the StatefulSet. The solution is to verify that the StatefulSet name is spelled correctly and exists in the same namespace as the HorizontalPodAutoscaler.
```
- Simple filter : `k8sgpt filters remove Service`
- Multiple filters : `k8sgpt filters remove Ingress,Pod`
**Anonymization does not currently apply to events.**
</details>
### Additional commands
<details>
<summary> Additional commands </summary>
_List configured backends_
```
k8sgpt auth list
```
_Update configured backends_
```
k8sgpt auth update $MY_BACKEND1,$MY_BACKEND2..
```
_Remove configured backends_
```
k8sgpt auth remove -b $MY_BACKEND1,$MY_BACKEND2..
```
_Manage integrations_
_List integrations_
@@ -287,190 +309,59 @@ k8sgpt serve
_Analysis with serve mode_
```
grpcurl -plaintext -d '{"namespace": "k8sgpt", "explain": false}' localhost:8080 schema.v1.ServerService/Analyze
curl -X GET "http://localhost:8080/analyze?namespace=k8sgpt&explain=false"
```
</details>
## LLM AI Backends
K8sGPT uses the chosen LLM, generative AI provider when you want to explain the analysis results using --explain flag e.g. `k8sgpt analyze --explain`. You can use `--backend` flag to specify a configured provider (it's `openai` by default).
You can list available providers using `k8sgpt auth list`:
```
Default:
> openai
Active:
Unused:
> openai
> localai
> azureopenai
> cohere
> amazonbedrock
> amazonsagemaker
> google
> noopai
```
For detailed documentation on how to configure and use each provider see [here](https://docs.k8sgpt.ai/reference/providers/backend/).
_To set a new default provider_
```
k8sgpt auth default -p azureopenai
Default provider set to azureopenai
```
## Key Features
<details>
With this option, the data is anonymized before being sent to the AI Backend. During the analysis execution, `k8sgpt` retrieves sensitive data (Kubernetes object names, labels, etc.). This data is masked when sent to the AI backend and replaced by a key that can be used to de-anonymize the data when the solution is returned to the user.
<summary> Anonymization </summary>
1. Error reported during analysis:
```bash
Error: HorizontalPodAutoscaler uses StatefulSet/fake-deployment as ScaleTargetRef which does not exist.
```
2. Payload sent to the AI backend:
```bash
Error: HorizontalPodAutoscaler uses StatefulSet/tGLcCRcHa1Ce5Rs as ScaleTargetRef which does not exist.
```
3. Payload returned by the AI:
```bash
The Kubernetes system is trying to scale a StatefulSet named tGLcCRcHa1Ce5Rs using the HorizontalPodAutoscaler, but it cannot find the StatefulSet. The solution is to verify that the StatefulSet name is spelled correctly and exists in the same namespace as the HorizontalPodAutoscaler.
```
4. Payload returned to the user:
```bash
The Kubernetes system is trying to scale a StatefulSet named fake-deployment using the HorizontalPodAutoscaler, but it cannot find the StatefulSet. The solution is to verify that the StatefulSet name is spelled correctly and exists in the same namespace as the HorizontalPodAutoscaler.
```
Note: **Anonymization does not currently apply to events.**
### Further Details
**Anonymization does not currently apply to events.**
*In a few analysers like Pod, we feed to the AI backend the event messages which are not known beforehand thus we are not masking them for the **time being**.*
- The following is the list of analysers in which data is **being masked**:-
- Statefulset
- Service
- PodDisruptionBudget
- Node
- NetworkPolicy
- Ingress
- HPA
- Deployment
- Cronjob
- The following is the list of analysers in which data is **not being masked**:-
- RepicaSet
- PersistentVolumeClaim
- Pod
- **_*Events_**
***Note**:
- k8gpt will not mask the above analysers because they do not send any identifying information except **Events** analyser.
- Masking for **Events** analyzer is scheduled in the near future as seen in this [issue](https://github.com/k8sgpt-ai/k8sgpt/issues/560). _Further research has to be made to understand the patterns and be able to mask the sensitive parts of an event like pod name, namespace etc._
- The following is the list of fields which are not **being masked**:-
- Describe
- ObjectStatus
- Replicas
- ContainerStatus
- **_*Event Message_**
- ReplicaStatus
- Count (Pod)
***Note**:
- It is quite possible the payload of the event message might have something like "super-secret-project-pod-X crashed" which we don't currently redact _(scheduled in the near future as seen in this [issue](https://github.com/k8sgpt-ai/k8sgpt/issues/560))_.
### Proceed with care
- The K8gpt team recommends using an entirely different backend **(a local model) in critical production environments**. By using a local model, you can rest assured that everything stays within your DMZ, and nothing is leaked.
- If there is any uncertainty about the possibility of sending data to a public LLM (open AI, Azure AI) and it poses a risk to business-critical operations, then, in such cases, the use of public LLM should be avoided based on personal assessment and the jurisdiction of risks involved.
</details>
## Running local models
To run local models, it is possible to use OpenAI compatible APIs, for instance [LocalAI](https://github.com/go-skynet/LocalAI) which uses [llama.cpp](https://github.com/ggerganov/llama.cpp) and [ggml](https://github.com/ggerganov/ggml) to run inference on consumer-grade hardware. Models supported by LocalAI for instance are Vicuna, Alpaca, LLaMA, Cerebras, GPT4ALL, GPT4ALL-J and koala.
<details>
<summary> Configuration management</summary>
To run local inference, you need to download the models first, for instance you can find `ggml` compatible models in [huggingface.com](https://huggingface.co/models?search=ggml) (for example vicuna, alpaca and koala).
### Start the API server
To start the API server, follow the instruction in [LocalAI](https://github.com/go-skynet/LocalAI#example-use-gpt4all-j-model).
### Run k8sgpt
To run k8sgpt, run `k8sgpt auth` with the `localai` backend:
```
k8sgpt auth --backend localai --model <model_name> --baseurl http://localhost:8080/v1
```
When being asked for an API key, just press enter.
Now you can analyze with the `localai` backend:
```
k8sgpt analyze --explain --backend localai
```
</details>
## Configuration
`k8sgpt` stores config data in the `$XDG_CONFIG_HOME/k8sgpt/k8sgpt.yaml` file. The data is stored in plain text, including your OpenAI key.
Config file locations:
| OS | Path |
| ------- | ------------------------------------------------ |
|---------|--------------------------------------------------|
| MacOS | ~/Library/Application Support/k8sgpt/k8sgpt.yaml |
| Linux | ~/.config/k8sgpt/k8sgpt.yaml |
| Windows | %LOCALAPPDATA%/k8sgpt/k8sgpt.yaml |
</details>
<details>
There may be scenarios where caching remotely is preferred.
In these scenarios K8sGPT supports AWS S3 or Azure Blob storage Integration.
<summary> Remote caching </summary>
<em>Note: You can only configure and use only one remote cache at a time</em>
_Adding a remote cache_
* AWS S3
* _As a prerequisite `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` are required as environmental variables._
* Configuration, ``` k8sgpt cache add s3 --region <aws region> --bucket <name> ```
* K8sGPT will create the bucket if it does not exist
* Azure Storage
* We support a number of [techniques](https://learn.microsoft.com/en-us/azure/developer/go/azure-sdk-authentication?tabs=bash#2-authenticate-with-azure) to authenticate against Azure
* Configuration, ``` k8sgpt cache add azure --storageacc <storage account name> --container <container name> ```
* K8sGPT assumes that the storage account already exist and it will create the container if it does not exist
* It is the **user** responsibility have to grant specific permissions to their identity in order to be able to upload blob files and create SA containers (e.g Storage Blob Data Contributor)
* Google Cloud Storage
* _As a prerequisite `GOOGLE_APPLICATION_CREDENTIALS` are required as environmental variables._
* Configuration, ``` k8sgpt cache add gcs --region <gcp region> --bucket <name> --projectid <project id>```
* K8sGPT will create the bucket if it does not exist
_Listing cache items_
```
k8sgpt cache list
```
_Purging an object from the cache_
Note: purging an object using this command will delete upstream files, so it requires appropriate permissions.
```
k8sgpt cache purge $OBJECT_NAME
```
_Removing the remote cache_
Note: this will not delete the upstream S3 bucket or Azure storage container
```
k8sgpt cache remove
```
</details>
## Documentation
Find our official documentation available [here](https://docs.k8sgpt.ai)
## Contributing
Please read our [contributing guide](./CONTRIBUTING.md).
## Community
Find us on [Slack](https://join.slack.com/t/k8sgpt/shared_invite/zt-276pa9uyq-pxAUr4TCVHubFxEvLZuT1Q)
Find us on [Slack](https://join.slack.com/t/k8sgpt/shared_invite/zt-1rwe5fpzq-VNtJK8DmYbbm~iWL1H34nw)
<a href="https://github.com/k8sgpt-ai/k8sgpt/graphs/contributors">
<img src="https://contrib.rocks/image?repo=k8sgpt-ai/k8sgpt" />
</a>
## License
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fk8sgpt-ai%2Fk8sgpt.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fk8sgpt-ai%2Fk8sgpt?ref=badge_large)

View File

@@ -8,4 +8,4 @@ For example if there is a vulnerability in release `0.1.0` we will fix that rele
## Reporting a Vulnerability
If you are aware of a vulnerability please feel free to disclose it responsibly to contact@k8sgpt.ai or to one of our maintainers in our Slack community.
If you are aware of a vulnverability please feel free to disclose it responsibly to contact@k8sgpt.ai or to one of our maintainers in our Slack community.

View File

@@ -1,6 +0,0 @@
apiVersion: v2
appVersion: v0.3.0 #x-release-please-version
description: A Helm chart for K8SGPT
name: k8sgpt
type: application
version: 1.0.0

View File

@@ -1,44 +0,0 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "k8sgpt.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "k8sgpt.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "k8sgpt.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "k8sgpt.labels" -}}
helm.sh/chart: {{ include "k8sgpt.chart" . }}
app.kubernetes.io/name: {{ include "k8sgpt.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion }}
{{- end }}
{{- end }}

View File

@@ -1,57 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ template "k8sgpt.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
{{- if .Values.deployment.annotations }}
annotations:
{{- toYaml .Values.deployment.annotations | nindent 4 }}
{{- end }}
labels:
{{- include "k8sgpt.labels" . | nindent 4 }}
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: {{ include "k8sgpt.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "k8sgpt.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
spec:
serviceAccountName: {{ template "k8sgpt.fullname" . }}
containers:
- name: k8sgpt-container
imagePullPolicy: {{ .Values.deployment.imagePullPolicy }}
image: {{ .Values.deployment.image.repository }}:{{ .Values.deployment.image.tag | default .Chart.AppVersion }}
ports:
- containerPort: 8080
args: ["serve"]
{{- if .Values.deployment.resources }}
resources:
{{- toYaml .Values.deployment.resources | nindent 10 }}
{{- end }}
env:
- name: K8SGPT_MODEL
value: {{ .Values.deployment.env.model }}
- name: K8SGPT_BACKEND
value: {{ .Values.deployment.env.backend }}
{{- if .Values.secret.secretKey }}
- name: K8SGPT_PASSWORD
valueFrom:
secretKeyRef:
name: ai-backend-secret
key: secret-key
{{- end }}
- name: XDG_CONFIG_HOME
value: /k8sgpt-config/
- name: XDG_CACHE_HOME
value: /k8sgpt-config/
volumeMounts:
- mountPath: /k8sgpt-config
name: config
volumes:
- emptyDir: {}
name: config

View File

@@ -1,15 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ template "k8sgpt.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "k8sgpt.labels" . | nindent 4 }}
subjects:
- kind: ServiceAccount
name: {{ template "k8sgpt.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
roleRef:
kind: ClusterRole
name: {{ template "k8sgpt.fullname" . }}
apiGroup: rbac.authorization.k8s.io

View File

@@ -1,7 +0,0 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ template "k8sgpt.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "k8sgpt.labels" . | nindent 4 }}

View File

@@ -1,10 +0,0 @@
{{- if .Values.secret.secretKey }}
apiVersion: v1
data:
secret-key: {{ .Values.secret.secretKey }}
kind: Secret
metadata:
name: ai-backend-secret
namespace: {{ .Release.Namespace | quote }}
type: Opaque
{{- end}}

View File

@@ -1,22 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: {{ template "k8sgpt.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "k8sgpt.labels" . | nindent 4 }}
{{- if .Values.service.annotations }}
annotations:
{{- toYaml .Values.service.annotations | nindent 4 }}
{{- end }}
spec:
selector:
app.kubernetes.io/name: {{ include "k8sgpt.name" . }}
ports:
- name: http
port: 8080
targetPort: 8080
- name: metrics
port: 8081
targetPort: 8081
type: {{ .Values.service.type }}

View File

@@ -1,21 +0,0 @@
{{- if .Values.serviceMonitor.enabled }}
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: {{ template "k8sgpt.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "k8sgpt.labels" . | nindent 4 }}
{{- if .Values.serviceMonitor.additionalLabels }}
{{- toYaml .Values.serviceMonitor.additionalLabels | nindent 4 }}
{{- end }}
spec:
endpoints:
- honorLabels: true
path: /metrics
port: metrics
selector:
matchLabels:
app.kubernetes.io/name: {{ include "k8sgpt.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

View File

@@ -1,27 +0,0 @@
deployment:
image:
repository: ghcr.io/k8sgpt-ai/k8sgpt
tag: "" # defaults to Chart.appVersion if unspecified
imagePullPolicy: Always
annotations: {}
env:
model: "gpt-3.5-turbo"
backend: "openai" # one of: [ openai | llama ]
resources:
limits:
cpu: "1"
memory: "512Mi"
requests:
cpu: "0.2"
memory: "156Mi"
secret:
secretKey: "" # base64 encoded OpenAI token
service:
type: ClusterIP
annotations: {}
serviceMonitor:
enabled: false
additionalLabels: {}

View File

@@ -16,27 +16,22 @@ package analyze
import (
"fmt"
"os"
"os/signal"
"syscall"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/ai/interactive"
"github.com/k8sgpt-ai/k8sgpt/pkg/analysis"
"github.com/spf13/cobra"
)
var (
explain bool
backend string
output string
filters []string
language string
nocache bool
namespace string
anonymize bool
maxConcurrency int
withDoc bool
interactiveMode bool
explain bool
backend string
output string
filters []string
language string
nocache bool
namespace string
anonymize bool
maxConcurrency int
)
// AnalyzeCmd represents the problems command
@@ -47,68 +42,42 @@ var AnalyzeCmd = &cobra.Command{
Long: `This command will find problems within your Kubernetes cluster and
provide you with a list of issues that need to be resolved`,
Run: func(cmd *cobra.Command, args []string) {
// Create analysis configuration first.
config, err := analysis.NewAnalysis(
backend,
language,
filters,
namespace,
nocache,
explain,
maxConcurrency,
withDoc,
interactiveMode,
)
// AnalysisResult configuration
config, err := analysis.NewAnalysis(backend,
language, filters, namespace, nocache, explain, maxConcurrency)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
defer config.Close()
config.RunAnalysis()
analysisErrors := config.RunAnalysis()
if len(analysisErrors) != 0 {
for _, err := range analysisErrors {
color.Red("Error: %s", err)
}
}
if explain {
if err := config.GetAIResults(output, anonymize); err != nil {
err := config.GetAIResults(output, anonymize)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
}
// print results
output_data, err := config.PrintOutput(output)
output, err := config.PrintOutput(output)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
fmt.Println(string(output_data))
if interactiveMode && explain {
if output == "json" {
color.Yellow("Caution: interactive mode using --json enabled may use additional tokens.")
}
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
interactiveClient := interactive.NewInteractionRunner(config, output_data)
go interactiveClient.StartInteraction()
for {
select {
case res := <-sigs:
switch res {
default:
os.Exit(0)
}
case res := <-interactiveClient.State:
switch res {
case interactive.E_EXITED:
os.Exit(0)
}
}
}
}
fmt.Println(string(output))
},
}
func init() {
// namespace flag
AnalyzeCmd.Flags().StringVarP(&namespace, "namespace", "n", "", "Namespace to analyze")
// no cache flag
@@ -127,8 +96,4 @@ func init() {
AnalyzeCmd.Flags().StringVarP(&language, "language", "l", "english", "Languages to use for AI (e.g. 'English', 'Spanish', 'French', 'German', 'Italian', 'Portuguese', 'Dutch', 'Russian', 'Chinese', 'Japanese', 'Korean')")
// add max concurrency
AnalyzeCmd.Flags().IntVarP(&maxConcurrency, "max-concurrency", "m", 10, "Maximum number of concurrent requests to the Kubernetes API server")
// kubernetes doc flag
AnalyzeCmd.Flags().BoolVarP(&withDoc, "with-doc", "d", false, "Give me the official documentation of the involved field")
// interactive mode flag
AnalyzeCmd.Flags().BoolVarP(&interactiveMode, "interactive", "i", false, "Enable interactive mode that allows further conversation with LLM about the problem. Works only with --explain flag")
}

View File

@@ -1,163 +0,0 @@
/*
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 auth
import (
"fmt"
"os"
"strings"
"syscall"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"golang.org/x/term"
)
const (
defaultBackend = "openai"
defaultModel = "gpt-3.5-turbo"
)
var addCmd = &cobra.Command{
Use: "add",
Short: "Add new provider",
Long: "The add command allows to configure a new backend AI provider",
PreRun: func(cmd *cobra.Command, args []string) {
backend, _ := cmd.Flags().GetString("backend")
if strings.ToLower(backend) == "azureopenai" {
_ = cmd.MarkFlagRequired("engine")
_ = cmd.MarkFlagRequired("baseurl")
}
if strings.ToLower(backend) == "amazonsagemaker" {
_ = cmd.MarkFlagRequired("endpointname")
_ = cmd.MarkFlagRequired("providerRegion")
}
},
Run: func(cmd *cobra.Command, args []string) {
// get ai configuration
err := viper.UnmarshalKey("ai", &configAI)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
// search for provider with same name
providerIndex := -1
for i, provider := range configAI.Providers {
if backend == provider.Name {
providerIndex = i
break
}
}
validBackend := func(validBackends []string, backend string) bool {
for _, b := range validBackends {
if b == backend {
return true
}
}
return false
}
// check if backend is not empty and a valid value
if backend == "" {
color.Yellow(fmt.Sprintf("Warning: backend input is empty, will use the default value: %s", defaultBackend))
backend = defaultBackend
} else {
if !validBackend(ai.Backends, backend) {
color.Red("Error: Backend AI accepted values are '%v'", strings.Join(ai.Backends, ", "))
os.Exit(1)
}
}
// check if model is not empty
if model == "" {
model = defaultModel
color.Yellow(fmt.Sprintf("Warning: model input is empty, will use the default value: %s", defaultModel))
}
if temperature > 1.0 || temperature < 0.0 {
color.Red("Error: temperature ranges from 0 to 1.")
os.Exit(1)
}
if topP > 1.0 || topP < 0.0 {
color.Red("Error: topP ranges from 0 to 1.")
os.Exit(1)
}
if ai.NeedPassword(backend) && password == "" {
fmt.Printf("Enter %s Key: ", backend)
bytePassword, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
color.Red("Error reading %s Key from stdin: %s", backend,
err.Error())
os.Exit(1)
}
password = strings.TrimSpace(string(bytePassword))
}
// create new provider object
newProvider := ai.AIProvider{
Name: backend,
Model: model,
Password: password,
BaseURL: baseURL,
EndpointName: endpointName,
Engine: engine,
Temperature: temperature,
ProviderRegion: providerRegion,
TopP: topP,
MaxTokens: maxTokens,
}
if providerIndex == -1 {
// provider with same name does not exist, add new provider to list
configAI.Providers = append(configAI.Providers, newProvider)
viper.Set("ai", configAI)
if err := viper.WriteConfig(); err != nil {
color.Red("Error writing config file: %s", err.Error())
os.Exit(1)
}
color.Green("%s added to the AI backend provider list", backend)
} else {
// provider with same name exists, update provider info
color.Yellow("Provider with same name already exists.")
}
},
}
func init() {
// add flag for backend
addCmd.Flags().StringVarP(&backend, "backend", "b", defaultBackend, "Backend AI provider")
// add flag for model
addCmd.Flags().StringVarP(&model, "model", "m", defaultModel, "Backend AI model")
// add flag for password
addCmd.Flags().StringVarP(&password, "password", "p", "", "Backend AI password")
// add flag for url
addCmd.Flags().StringVarP(&baseURL, "baseurl", "u", "", "URL AI provider, (e.g `http://localhost:8080/v1`)")
// add flag for endpointName
addCmd.Flags().StringVarP(&endpointName, "endpointname", "n", "", "Endpoint Name, e.g. `endpoint-xxxxxxxxxxxx` (only for amazonbedrock, amazonsagemaker backends)")
// add flag for topP
addCmd.Flags().Float32VarP(&topP, "topp", "c", 0.5, "Probability Cutoff: Set a threshold (0.0-1.0) to limit word choices. Higher values add randomness, lower values increase predictability.")
// max tokens
addCmd.Flags().IntVarP(&maxTokens, "maxtokens", "l", 2048, "Specify a maximum output length. Adjust (1-...) to control text length. Higher values produce longer output, lower values limit length")
// add flag for temperature
addCmd.Flags().Float32VarP(&temperature, "temperature", "t", 0.7, "The sampling temperature, value ranges between 0 ( output be more deterministic) and 1 (more random)")
// add flag for azure open ai engine/deployment name
addCmd.Flags().StringVarP(&engine, "engine", "e", "", "Azure AI deployment name (only for azureopenai backend)")
//add flag for amazonbedrock region name
addCmd.Flags().StringVarP(&providerRegion, "providerRegion", "r", "", "Provider Region name (only for amazonbedrock backend)")
}

View File

@@ -14,47 +14,107 @@ limitations under the License.
package auth
import (
"fmt"
"os"
"strings"
"syscall"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"golang.org/x/term"
)
var (
backend string
password string
baseURL string
endpointName string
model string
engine string
temperature float32
providerRegion string
topP float32
maxTokens int
backend string
password string
baseURL string
model string
)
var configAI ai.AIConfiguration
// authCmd represents the auth command
var AuthCmd = &cobra.Command{
Use: "auth",
Short: "Authenticate with your chosen backend",
Long: `Provide the necessary credentials to authenticate with your chosen backend.`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
_ = cmd.Help()
return
// get ai configuration
var configAI ai.AIConfiguration
err := viper.UnmarshalKey("ai", &configAI)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
// search for provider with same name
providerIndex := -1
for i, provider := range configAI.Providers {
if backend == provider.Name {
providerIndex = i
break
}
}
// check if backend is not empty
if backend == "" {
color.Red("Error: Backend AI cannot be empty.")
os.Exit(1)
}
color.Green("Using %s as backend AI provider", backend)
// check if model is not empty
if model == "" {
color.Red("Error: Model cannot be empty.")
os.Exit(1)
}
if ai.NeedPassword(backend) && password == "" {
fmt.Printf("Enter %s Key: ", backend)
bytePassword, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
color.Red("Error reading %s Key from stdin: %s", backend,
err.Error())
os.Exit(1)
}
password = strings.TrimSpace(string(bytePassword))
}
// create new provider object
newProvider := ai.AIProvider{
Name: backend,
Model: model,
Password: password,
BaseURL: baseURL,
}
if providerIndex == -1 {
// provider with same name does not exist, add new provider to list
configAI.Providers = append(configAI.Providers, newProvider)
color.Green("New provider added")
} else {
// provider with same name exists, update provider info
configAI.Providers[providerIndex] = newProvider
color.Green("Provider updated")
}
viper.Set("ai", configAI)
if err := viper.WriteConfig(); err != nil {
color.Red("Error writing config file: %s", err.Error())
os.Exit(1)
}
color.Green("key added")
},
}
func init() {
// add subcommand to list backends
AuthCmd.AddCommand(listCmd)
// add subcommand to create new backend provider
AuthCmd.AddCommand(addCmd)
// add subcommand to remove new backend provider
AuthCmd.AddCommand(removeCmd)
// add subcommand to set default backend provider
AuthCmd.AddCommand(defaultCmd)
// add subcommand to update backend provider
AuthCmd.AddCommand(updateCmd)
// add flag for backend
AuthCmd.Flags().StringVarP(&backend, "backend", "b", "openai", "Backend AI provider")
// add flag for model
AuthCmd.Flags().StringVarP(&model, "model", "m", "gpt-3.5-turbo", "Backend AI model")
// add flag for password
AuthCmd.Flags().StringVarP(&password, "password", "p", "", "Backend AI password")
// add flag for url
AuthCmd.Flags().StringVarP(&baseURL, "baseurl", "u", "", "URL AI provider, (e.g `http://localhost:8080/v1`)")
}

View File

@@ -1,79 +0,0 @@
/*
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 auth
import (
"os"
"strings"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var (
providerName string
)
var defaultCmd = &cobra.Command{
Use: "default",
Short: "Set your default AI backend provider",
Long: "The command to set your new default AI backend provider (default is openai)",
Run: func(cmd *cobra.Command, args []string) {
err := viper.UnmarshalKey("ai", &configAI)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
if providerName == "" {
if configAI.DefaultProvider != "" {
color.Yellow("Your default provider is %s", configAI.DefaultProvider)
} else {
color.Yellow("Your default provider is openai")
}
os.Exit(0)
}
// lowercase the provider name
providerName = strings.ToLower(providerName)
// Check if the provider is in the provider list
providerExists := false
for _, provider := range configAI.Providers {
if provider.Name == providerName {
providerExists = true
}
}
if !providerExists {
color.Red("Error: Provider %s does not exist", providerName)
os.Exit(1)
}
// Set the default provider
configAI.DefaultProvider = providerName
viper.Set("ai", configAI)
// Viper write config
err = viper.WriteConfig()
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
// Print acknowledgement
color.Green("Default provider set to %s", providerName)
},
}
func init() {
// provider name flag
defaultCmd.Flags().StringVarP(&providerName, "provider", "p", "", "The name of the provider to set as default")
}

View File

@@ -1,104 +0,0 @@
/*
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 auth
import (
"fmt"
"os"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var details bool
var userInput string
var listCmd = &cobra.Command{
Use: "list",
Short: "List configured providers",
Long: "The list command displays a list of configured providers",
Run: func(cmd *cobra.Command, args []string) {
// get ai configuration
err := viper.UnmarshalKey("ai", &configAI)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
if details {
fmt.Println("Show password ? (y/n)")
fmt.Scan(&userInput)
}
// Print the default if it is set
fmt.Print(color.YellowString("Default: \n"))
if configAI.DefaultProvider != "" {
fmt.Printf("> %s\n", color.BlueString(configAI.DefaultProvider))
} else {
fmt.Printf("> %s\n", color.BlueString("openai"))
}
// Get list of all AI Backends and only print them if they are not in the provider list
fmt.Print(color.YellowString("Active: \n"))
for _, aiBackend := range ai.Backends {
providerExists := false
for _, provider := range configAI.Providers {
if provider.Name == aiBackend {
providerExists = true
}
}
if providerExists {
fmt.Printf("> %s\n", color.GreenString(aiBackend))
if details {
for _, provider := range configAI.Providers {
if provider.Name == aiBackend {
printDetails(provider, userInput)
}
}
}
}
}
fmt.Print(color.YellowString("Unused: \n"))
for _, aiBackend := range ai.Backends {
providerExists := false
for _, provider := range configAI.Providers {
if provider.Name == aiBackend {
providerExists = true
}
}
if !providerExists {
fmt.Printf("> %s\n", color.RedString(aiBackend))
}
}
},
}
func init() {
listCmd.Flags().BoolVar(&details, "details", false, "Print active provider configuration details")
}
func printDetails(provider ai.AIProvider, userInput string) {
if provider.Model != "" {
fmt.Printf(" - Model: %s\n", provider.Model)
}
if provider.Engine != "" {
fmt.Printf(" - Engine: %s\n", provider.Engine)
}
if provider.BaseURL != "" {
fmt.Printf(" - BaseURL: %s\n", provider.BaseURL)
}
}

View File

@@ -1,77 +0,0 @@
/*
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 auth
import (
"os"
"strings"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var removeCmd = &cobra.Command{
Use: "remove",
Short: "Remove provider(s)",
Long: "The command to remove AI backend provider(s)",
PreRun: func(cmd *cobra.Command, args []string) {
_ = cmd.MarkFlagRequired("backends")
},
Run: func(cmd *cobra.Command, args []string) {
if backend == "" {
color.Red("Error: backends must be set.")
_ = cmd.Help()
return
}
inputBackends := strings.Split(backend, ",")
err := viper.UnmarshalKey("ai", &configAI)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
for _, b := range inputBackends {
foundBackend := false
for i, provider := range configAI.Providers {
if b == provider.Name {
foundBackend = true
configAI.Providers = append(configAI.Providers[:i], configAI.Providers[i+1:]...)
if configAI.DefaultProvider == b {
configAI.DefaultProvider = "openai"
}
color.Green("%s deleted from the AI backend provider list", b)
break
}
}
if !foundBackend {
color.Red("Error: %s does not exist in configuration file. Please use k8sgpt auth new.", b)
os.Exit(1)
}
}
viper.Set("ai", configAI)
if err := viper.WriteConfig(); err != nil {
color.Red("Error writing config file: %s", err.Error())
os.Exit(1)
}
},
}
func init() {
// add flag for backends
removeCmd.Flags().StringVarP(&backend, "backends", "b", "", "Backend AI providers to remove (separated by a comma)")
}

View File

@@ -1,113 +0,0 @@
/*
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 auth
import (
"os"
"strings"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var updateCmd = &cobra.Command{
Use: "update",
Short: "Update a backend provider",
Long: "The command to update an AI backend provider",
Args: cobra.ExactArgs(1),
PreRun: func(cmd *cobra.Command, args []string) {
backend, _ := cmd.Flags().GetString("backend")
if strings.ToLower(backend) == "azureopenai" {
_ = cmd.MarkFlagRequired("engine")
_ = cmd.MarkFlagRequired("baseurl")
}
},
Run: func(cmd *cobra.Command, args []string) {
// get ai configuration
err := viper.UnmarshalKey("ai", &configAI)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
inputBackends := strings.Split(args[0], ",")
if len(inputBackends) == 0 {
color.Red("Error: backend must be set.")
os.Exit(1)
}
if temperature > 1.0 || temperature < 0.0 {
color.Red("Error: temperature ranges from 0 to 1.")
os.Exit(1)
}
for _, b := range inputBackends {
foundBackend := false
for i, provider := range configAI.Providers {
if b == provider.Name {
foundBackend = true
if backend != "" {
configAI.Providers[i].Name = backend
color.Blue("Backend name updated successfully")
}
if model != "" {
configAI.Providers[i].Model = model
color.Blue("Model updated successfully")
}
if password != "" {
configAI.Providers[i].Password = password
color.Blue("Password updated successfully")
}
if baseURL != "" {
configAI.Providers[i].BaseURL = baseURL
color.Blue("Base URL updated successfully")
}
if engine != "" {
configAI.Providers[i].Engine = engine
}
configAI.Providers[i].Temperature = temperature
color.Green("%s updated in the AI backend provider list", b)
}
}
if !foundBackend {
color.Red("Error: %s does not exist in configuration file. Please use k8sgpt auth new.", args[0])
os.Exit(1)
}
}
viper.Set("ai", configAI)
if err := viper.WriteConfig(); err != nil {
color.Red("Error writing config file: %s", err.Error())
os.Exit(1)
}
},
}
func init() {
// update flag for backend
updateCmd.Flags().StringVarP(&backend, "backend", "b", "", "Update backend AI provider")
// update flag for model
updateCmd.Flags().StringVarP(&model, "model", "m", "", "Update backend AI model")
// update flag for password
updateCmd.Flags().StringVarP(&password, "password", "p", "", "Update backend AI password")
// update flag for url
updateCmd.Flags().StringVarP(&baseURL, "baseurl", "u", "", "Update URL AI provider, (e.g `http://localhost:8080/v1`)")
// add flag for temperature
updateCmd.Flags().Float32VarP(&temperature, "temperature", "t", 0.7, "The sampling temperature, value ranges between 0 ( output be more deterministic) and 1 (more random)")
// update flag for azure open ai engine/deployment name
updateCmd.Flags().StringVarP(&engine, "engine", "e", "", "Update Azure AI deployment name")
}

79
cmd/cache/add.go vendored
View File

@@ -1,79 +0,0 @@
/*
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 cache
import (
"fmt"
"os"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
"github.com/spf13/cobra"
)
var (
region string
bucketName string
storageAccount string
containerName string
projectId string
)
// addCmd represents the add command
var addCmd = &cobra.Command{
Use: "add [cache type]",
Short: "Add a remote cache",
Long: `This command allows you to add a remote cache to store the results of an analysis.
The supported cache types are:
- Azure Blob storage
- Google Cloud storage
- S3`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
color.Red("Error: Please provide a value for cache types. Run k8sgpt cache add --help")
os.Exit(1)
}
fmt.Println(color.YellowString("Adding remote based cache"))
cacheType := args[0]
remoteCache, err := cache.NewCacheProvider(cacheType, bucketname, region, storageAccount, containerName, projectId)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
err = cache.AddRemoteCache(remoteCache)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
},
}
func init() {
CacheCmd.AddCommand(addCmd)
addCmd.Flags().StringVarP(&region, "region", "r", "", "The region to use for the AWS S3 or GCS cache")
addCmd.Flags().StringVarP(&bucketname, "bucket", "b", "", "The name of the AWS S3 bucket to use for the cache")
addCmd.MarkFlagsRequiredTogether("region", "bucket")
addCmd.Flags().StringVarP(&projectId, "projectid", "p", "", "The GCP project ID")
addCmd.Flags().StringVarP(&storageAccount, "storageacc", "s", "", "The Azure storage account name of the container")
addCmd.Flags().StringVarP(&containerName, "container", "c", "", "The Azure container name to use for the cache")
addCmd.MarkFlagsRequiredTogether("storageacc", "container")
// Tedious check to ensure we don't include arguments from different providers
addCmd.MarkFlagsMutuallyExclusive("region", "storageacc")
addCmd.MarkFlagsMutuallyExclusive("region", "container")
addCmd.MarkFlagsMutuallyExclusive("bucket", "storageacc")
addCmd.MarkFlagsMutuallyExclusive("bucket", "container")
addCmd.MarkFlagsMutuallyExclusive("projectid", "storageacc")
addCmd.MarkFlagsMutuallyExclusive("projectid", "container")
}

39
cmd/cache/cache.go vendored
View File

@@ -1,39 +0,0 @@
/*
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 cache
import (
"github.com/spf13/cobra"
)
var (
bucketname string
)
// cacheCmd represents the cache command
var CacheCmd = &cobra.Command{
Use: "cache",
Short: "For working with the cache the results of an analysis",
Long: `Cache commands allow you to add a remote cache, list the contents of the cache, and remove items from the cache.`,
Run: func(cmd *cobra.Command, args []string) {
err := cmd.Help()
if err != nil {
panic(err)
}
},
}
func init() {
}

67
cmd/cache/list.go vendored
View File

@@ -1,67 +0,0 @@
/*
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 cache
import (
"os"
"reflect"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
)
// listCmd represents the list command
var listCmd = &cobra.Command{
Use: "list",
Short: "List the contents of the cache",
Long: `This command allows you to list the contents of the cache.`,
Run: func(cmd *cobra.Command, args []string) {
// load remote cache if it is configured
c, err := cache.GetCacheConfiguration()
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
names, err := c.List()
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
var headers []string
obj := cache.CacheObjectDetails{}
objType := reflect.TypeOf(obj)
for i := 0; i < objType.NumField(); i++ {
field := objType.Field(i)
headers = append(headers, field.Name)
}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader(headers)
for _, v := range names {
table.Append([]string{v.Name, v.UpdatedAt.String()})
}
table.Render()
},
}
func init() {
CacheCmd.AddCommand(listCmd)
}

54
cmd/cache/purge.go vendored
View File

@@ -1,54 +0,0 @@
/*
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 cache
import (
"fmt"
"os"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
"github.com/spf13/cobra"
)
var purgeCmd = &cobra.Command{
Use: "purge [object name]",
Short: "Purge a remote cache",
Long: "This command allows you to delete/purge one object from the cache",
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
color.Red("Error: Please provide a value for object name. Run k8sgpt cache purge --help")
os.Exit(1)
}
objectKey := args[0]
fmt.Println(color.YellowString("Purging a remote cache."))
c, err := cache.GetCacheConfiguration()
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
err = c.Remove(objectKey)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
fmt.Println(color.GreenString("Object deleted."))
},
}
func init() {
CacheCmd.AddCommand(purgeCmd)
}

43
cmd/cache/remove.go vendored
View File

@@ -1,43 +0,0 @@
/*
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 cache
import (
"os"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
"github.com/spf13/cobra"
)
// removeCmd represents the remove command
var removeCmd = &cobra.Command{
Use: "remove",
Short: "Remove the remote cache",
Long: `This command allows you to remove the remote cache and use the default filecache.`,
Run: func(cmd *cobra.Command, args []string) {
err := cache.RemoveRemoteCache()
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
color.Green("Successfully removed the remote cache")
},
}
func init() {
CacheCmd.AddCommand(removeCmd)
}

View File

@@ -45,13 +45,6 @@ var addCmd = &cobra.Command{
for _, filter := range availableFilters {
if filter == f {
foundFilter = true
// WARNING: This is to enable users correctly understand implications
// of enabling logs
if filter == "Log" {
color.Yellow("Warning: by enabling logs, you will be sending potentially sensitive data to the AI backend.")
}
break
}
}

View File

@@ -25,7 +25,7 @@ var FiltersCmd = &cobra.Command{
You can list available filters to analyze resources.`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
_ = cmd.Help()
cmd.Help()
return
}
},

View File

@@ -18,7 +18,6 @@ import (
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer"
"github.com/k8sgpt-ai/k8sgpt/pkg/integration"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
"github.com/spf13/cobra"
"github.com/spf13/viper"
@@ -31,33 +30,27 @@ var listCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
activeFilters := viper.GetStringSlice("active_filters")
coreFilters, additionalFilters, integrationFilters := analyzer.ListFilters()
integration := integration.NewIntegration()
availableFilters := append(append(coreFilters, additionalFilters...), integrationFilters...)
if len(activeFilters) == 0 {
activeFilters = coreFilters
}
inactiveFilters := util.SliceDiff(availableFilters, activeFilters)
fmt.Print(color.YellowString("Active: \n"))
fmt.Printf(color.YellowString("Active: \n"))
for _, filter := range activeFilters {
// if the filter is an integration, mark this differently
// but if the integration is inactive, remove
if util.SliceContainsString(integrationFilters, filter) {
fmt.Printf("> %s\n", color.BlueString("%s (integration)", filter))
} else {
// This strange bit of logic will loop through every integration via
// OwnsAnalyzer subcommand to check the filter and as the integrationFilters...
// was no match, we know this isn't part of an active integration
if _, err := integration.AnalyzerByIntegration(filter); err != nil {
fmt.Printf("> %s\n", color.GreenString(filter))
}
fmt.Printf("> %s\n", color.GreenString(filter))
}
}
// display inactive filters
if len(inactiveFilters) != 0 {
fmt.Print(color.YellowString("Unused: \n"))
fmt.Printf(color.YellowString("Unused: \n"))
for _, filter := range inactiveFilters {
// if the filter is an integration, mark this differently
if util.SliceContainsString(integrationFilters, filter) {

View File

@@ -15,12 +15,11 @@ package generate
import (
"fmt"
"os/exec"
"runtime"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"os/exec"
"runtime"
)
var (
@@ -86,6 +85,6 @@ func printInstructions(isGui bool, backendType string) {
color.Green("Please open: https://beta.openai.com/account/api-keys to generate a key for %s", backendType)
fmt.Println("")
}
color.Green("Please copy the generated key and run `k8sgpt auth add` to add it to your config file")
color.Green("Please copy the generated key and run `k8sgpt auth` to add it to your config file")
fmt.Println("")
}

View File

@@ -15,14 +15,8 @@ package integration
import (
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer"
"github.com/k8sgpt-ai/k8sgpt/pkg/integration"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var (
skipInstall bool
)
// activateCmd represents the activate command
@@ -33,17 +27,10 @@ var activateCmd = &cobra.Command{
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
integrationName := args[0]
coreFilters, _, _ := analyzer.ListFilters()
// Update filters
activeFilters := viper.GetStringSlice("active_filters")
if len(activeFilters) == 0 {
activeFilters = coreFilters
}
integration := integration.NewIntegration()
// Check if the integation exists
err := integration.Activate(integrationName, namespace, activeFilters, skipInstall)
err := integration.Activate(integrationName, namespace)
if err != nil {
color.Red("Error: %v", err)
return
@@ -55,6 +42,5 @@ var activateCmd = &cobra.Command{
func init() {
IntegrationCmd.AddCommand(activateCmd)
activateCmd.Flags().BoolVarP(&skipInstall, "no-install", "s", false, "Only activate the integration filter without installing the filter (for example, if that filter plugin is already deployed in cluster, we do not need to re-install it again)")
}

View File

@@ -25,14 +25,14 @@ var (
var IntegrationCmd = &cobra.Command{
Use: "integration",
Aliases: []string{"integrations"},
Short: "Integrate another tool into K8sGPT",
Long: `Integrate another tool into K8sGPT. For example:
Short: "Intergrate another tool into K8sGPT",
Long: `Intergrate another tool into K8sGPT. For example:
k8sgpt integration activate trivy
This would allow you to deploy trivy into your cluster and use a K8sGPT analyzer to parse trivy results.`,
Run: func(cmd *cobra.Command, args []string) {
_ = cmd.Help()
cmd.Help()
},
}

View File

@@ -19,9 +19,9 @@ import (
"path/filepath"
"github.com/adrg/xdg"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/cmd/analyze"
"github.com/k8sgpt-ai/k8sgpt/cmd/auth"
"github.com/k8sgpt-ai/k8sgpt/cmd/cache"
"github.com/k8sgpt-ai/k8sgpt/cmd/filters"
"github.com/k8sgpt-ai/k8sgpt/cmd/generate"
"github.com/k8sgpt-ai/k8sgpt/cmd/integration"
@@ -35,9 +35,7 @@ var (
cfgFile string
kubecontext string
kubeconfig string
Version string
Commit string
Date string
version string
)
// rootCmd represents the base command when called without any subcommands
@@ -52,10 +50,8 @@ var rootCmd = &cobra.Command{
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute(v string, c string, d string) {
Version = v
Commit = c
Date = d
func Execute(v string) {
version = v
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
@@ -73,8 +69,7 @@ func init() {
rootCmd.AddCommand(generate.GenerateCmd)
rootCmd.AddCommand(integration.IntegrationCmd)
rootCmd.AddCommand(serve.ServeCmd)
rootCmd.AddCommand(cache.CacheCmd)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", fmt.Sprintf("Default config file (%s/k8sgpt/k8sgpt.yaml)", xdg.ConfigHome))
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.k8sgpt.yaml)")
rootCmd.PersistentFlags().StringVar(&kubecontext, "kubecontext", "", "Kubernetes context to use. Only required if out-of-cluster.")
rootCmd.PersistentFlags().StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
}
@@ -92,7 +87,7 @@ func initConfig() {
viper.SetConfigType("yaml")
viper.SetConfigName("k8sgpt")
_ = viper.SafeWriteConfig()
viper.SafeWriteConfig()
}
viper.Set("kubecontext", kubecontext)
@@ -103,7 +98,6 @@ func initConfig() {
// If a config file is found, read it in.
if err := viper.ReadInConfig(); err == nil {
_ = 1
// fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
}
}
@@ -122,7 +116,14 @@ func performConfigMigrationIfNeeded() {
err = util.EnsureDirExists(configDir)
cobra.CheckErr(err)
if oldConfigExists && newConfigExists {
fmt.Fprintln(os.Stderr, color.RedString("Warning: Legacy config file at `%s` detected! This file will be ignored!", oldConfig))
return
}
if oldConfigExists && !newConfigExists {
fmt.Fprintln(os.Stderr, color.RedString("Performing config file migration from `%s` to `%s`", oldConfig, newConfig))
err = os.Rename(oldConfig, newConfig)
cobra.CheckErr(err)
}

View File

@@ -15,25 +15,17 @@ package serve
import (
"os"
"strconv"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
k8sgptserver "github.com/k8sgpt-ai/k8sgpt/pkg/server"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"go.uber.org/zap"
)
const (
defaultTemperature float32 = 0.7
)
var (
port string
metricsPort string
backend string
enableHttp bool
port string
backend string
)
var ServeCmd = &cobra.Command{
@@ -50,40 +42,22 @@ var ServeCmd = &cobra.Command{
}
var aiProvider *ai.AIProvider
if len(configAI.Providers) == 0 {
// we validate and set temperature for our backend
temperature := func() float32 {
env := os.Getenv("K8SGPT_TEMPERATURE")
if env == "" {
return defaultTemperature
}
temperature, err := strconv.ParseFloat(env, 32)
if err != nil {
color.Red("Unable to convert Temperature value: %v", err)
os.Exit(1)
}
if temperature > 1.0 || temperature < 0.0 {
color.Red("Error: temperature ranges from 0 to 1.")
os.Exit(1)
}
return float32(temperature)
}
// Check for env injection
backend = os.Getenv("K8SGPT_BACKEND")
password := os.Getenv("K8SGPT_PASSWORD")
model := os.Getenv("K8SGPT_MODEL")
baseURL := os.Getenv("K8SGPT_BASEURL")
engine := os.Getenv("K8SGPT_ENGINE")
// If the envs are set, allocate in place to the aiProvider
// else exit with error
envIsSet := backend != "" || password != "" || model != ""
envIsSet := backend != "" || password != "" || model != "" || baseURL != ""
if envIsSet {
aiProvider = &ai.AIProvider{
Name: backend,
Password: password,
Model: model,
BaseURL: baseURL,
Engine: engine,
Temperature: temperature(),
Name: backend,
Password: password,
Model: model,
BaseURL: baseURL,
}
configAI.Providers = append(configAI.Providers, *aiProvider)
@@ -101,10 +75,10 @@ var ServeCmd = &cobra.Command{
if aiProvider == nil {
for _, provider := range configAI.Providers {
if backend == provider.Name {
// the pointer to the range variable is not really an issue here, as there
// is a break right after, but to prevent potential future issues, a temp
// variable is assigned
p := provider
// he pointer to the range variable is not really an issue here, as there
// is a break right after, but to prevent potential future issues, a temp
// variable is assigned
p := provider
aiProvider = &p
break
}
@@ -116,44 +90,23 @@ var ServeCmd = &cobra.Command{
os.Exit(1)
}
logger, err := zap.NewProduction()
server := k8sgptserver.Config{
Backend: aiProvider.Name,
Port: port,
Token: aiProvider.Password,
}
err = server.Serve()
if err != nil {
color.Red("failed to create logger: %v", err)
color.Red("Error: %v", err)
os.Exit(1)
}
defer logger.Sync()
server := k8sgptserver.Config{
Backend: aiProvider.Name,
Port: port,
MetricsPort: metricsPort,
EnableHttp: enableHttp,
Token: aiProvider.Password,
Logger: logger,
}
go func() {
if err := server.ServeMetrics(); err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
}()
go func() {
if err := server.Serve(); err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
}()
// Wait for both servers to exit
select {}
// override the default backend if a flag is provided
},
}
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(&backend, "backend", "b", "openai", "Backend AI provider")
ServeCmd.Flags().BoolVarP(&enableHttp, "http", "h", false, "Enable REST/http using gppc-gateway")
}

View File

@@ -14,9 +14,6 @@ limitations under the License.
package cmd
import (
"fmt"
"runtime/debug"
"github.com/spf13/cobra"
)
@@ -26,21 +23,7 @@ var versionCmd = &cobra.Command{
Short: "Print the version number of k8sgpt",
Long: `All software has versions. This is k8sgpt's`,
Run: func(cmd *cobra.Command, args []string) {
if Version == "dev" {
details, ok := debug.ReadBuildInfo()
if ok && details.Main.Version != "" && details.Main.Version != "(devel)" {
Version = details.Main.Version
for _, i := range details.Settings {
if i.Key == "vcs.time" {
Date = i.Value
}
if i.Key == "vcs.revision" {
Commit = i.Value
}
}
}
}
fmt.Printf("k8sgpt: %s (%s), built at: %s\n", Version, Commit, Date)
cmd.Printf("k8sgpt version %s\n", version)
},
}

View File

@@ -9,12 +9,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
FROM golang:1.21-alpine3.19 AS builder
FROM golang:1.20.3-alpine3.16 AS builder
ENV CGO_ENABLED=0
ARG VERSION
ARG COMMIT
ARG DATE
WORKDIR /workspace
COPY go.mod go.sum ./
@@ -22,7 +20,7 @@ RUN go mod download
COPY ./ ./
RUN go build -o /workspace/k8sgpt -ldflags "-X main.version=${VERSION} -X main.commit=${COMMIT} -X main.date=${DATE}" ./
RUN go build -o /workspace/k8sgpt ./
FROM gcr.io/distroless/static AS production
@@ -36,4 +34,4 @@ WORKDIR /
COPY --from=builder /workspace/k8sgpt .
USER 65532:65532
ENTRYPOINT ["/k8sgpt"]
ENTRYPOINT ["/k8sgpt"]

View File

@@ -47,7 +47,7 @@
"showLineNumbers": false,
"showMiniMap": false
},
"content": "![K8sGPT Logo](https://k8sgpt.ai/images/logo-black.png)",
"content": "![K8sGPT Logo](https://k8sgpt.ai/images/logo-white.png)",
"mode": "markdown"
},
"pluginVersion": "9.4.7",

View File

@@ -0,0 +1,42 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: k8sgpt-deployment
namespace: k8sgpt
labels:
app: k8sgpt
spec:
replicas: 1
selector:
matchLabels:
app: k8sgpt
template:
metadata:
labels:
app: k8sgpt
spec:
serviceAccountName: k8sgpt
containers:
- name: k8sgpt-container
imagePullPolicy: Always
image: ghcr.io/k8sgpt-ai/k8sgpt:v0.2.8 #x-release-please-version
ports:
- containerPort: 8080
args: ["serve"]
resources:
limits:
cpu: "1"
memory: "512Mi"
requests:
cpu: "0.2"
memory: "156Mi"
env:
- name: K8SGPT_MODEL
value: "gpt-3.5-turbo"
- name: K8SGPT_BACKEND
value: "openai"
- name: K8SGPT_PASSWORD
valueFrom:
secretKeyRef:
name: ai-backend-secret
key: secret-key

View File

@@ -1,10 +1,7 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ template "k8sgpt.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "k8sgpt.labels" . | nindent 4 }}
name: k8sgpt-cluster-role-all
rules:
- apiGroups:
- '*'

View File

@@ -0,0 +1,13 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: k8sgpt-rolebinding
namespace: k8sgpt
subjects:
- kind: ServiceAccount
name: k8sgpt
namespace: k8sgpt
roleRef:
kind: ClusterRole
name: k8sgpt-cluster-role-all
apiGroup: rbac.authorization.k8s.io

View File

@@ -0,0 +1,5 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: k8sgpt
namespace: k8sgpt

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: k8sgpt-service
namespace: k8sgpt
labels:
app: k8sgpt
spec:
selector:
app: k8sgpt
ports:
- name: http
port: 8080
targetPort: 8080
type: ClusterIP

View File

@@ -0,0 +1,15 @@
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
labels:
app: k8sgpt
name: k8sgpt-service-monitor
namespace: k8sgpt
spec:
endpoints:
- honorLabels: true
path: /metrics
port: http
selector:
matchLabels:
app: k8sgpt

270
go.mod
View File

@@ -1,152 +1,78 @@
module github.com/k8sgpt-ai/k8sgpt
go 1.21
go 1.20
require (
github.com/aquasecurity/trivy-operator v0.17.1
github.com/fatih/color v1.16.0
github.com/aquasecurity/trivy-operator v0.13.1
github.com/fatih/color v1.15.0
github.com/magiconair/properties v1.8.7
github.com/mittwald/go-helm-client v0.12.5
github.com/prometheus/prometheus v1.8.2-0.20211119115433-692a54649ed7
github.com/sashabaranov/go-openai v1.18.2
github.com/schollz/progressbar/v3 v3.14.1
github.com/spf13/cobra v1.8.0
github.com/spf13/viper v1.18.2
github.com/stretchr/testify v1.8.4
golang.org/x/term v0.16.0
helm.sh/helm/v3 v3.13.3
k8s.io/api v0.28.4
k8s.io/apimachinery v0.28.4
k8s.io/client-go v0.28.4
k8s.io/kubectl v0.28.4 // indirect
github.com/mittwald/go-helm-client v0.12.1
github.com/sashabaranov/go-openai v1.9.0
github.com/schollz/progressbar/v3 v3.13.1
github.com/spf13/cobra v1.7.0
github.com/spf13/viper v1.15.0
github.com/stretchr/testify v1.8.2
golang.org/x/term v0.7.0
helm.sh/helm/v3 v3.11.3
k8s.io/api v0.26.3
k8s.io/apimachinery v0.26.3
k8s.io/client-go v0.26.3
k8s.io/kubectl v0.26.3
)
require github.com/adrg/xdg v0.4.0
require (
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.3.0-20240208090903-d4147b284845.2
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.32.0-20240208090903-d4147b284845.1
cloud.google.com/go/storage v1.36.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.1
github.com/aws/aws-sdk-go v1.49.21
github.com/cohere-ai/cohere-go v0.2.0
github.com/google/generative-ai-go v0.5.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1
github.com/olekukonko/tablewriter v0.0.5
github.com/pterm/pterm v0.12.74
google.golang.org/api v0.155.0
gopkg.in/yaml.v2 v2.4.0
sigs.k8s.io/controller-runtime v0.16.3
sigs.k8s.io/gateway-api v1.0.0
)
require (
atomicgo.dev/cursor v0.2.0 // indirect
atomicgo.dev/keyboard v0.2.9 // indirect
atomicgo.dev/schedule v0.1.0 // indirect
cloud.google.com/go v0.111.0 // indirect
cloud.google.com/go/ai v0.3.0 // indirect
cloud.google.com/go/compute v1.23.3 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v1.1.5 // indirect
cloud.google.com/go/longrunning v0.5.4 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0 // indirect
github.com/Microsoft/hcsshim v0.11.4 // indirect
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
github.com/anchore/go-struct-converter v0.0.0-20230627203149-c72ef8859ca9 // indirect
github.com/cohere-ai/tokenizer v1.1.1 // indirect
github.com/containerd/console v1.0.3 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/distribution/reference v0.5.0 // indirect
github.com/dlclark/regexp2 v1.10.0 // indirect
github.com/evanphx/json-patch/v5 v5.7.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
github.com/golang-jwt/jwt/v5 v5.0.0 // 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.7 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jpillora/backoff v1.0.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/lithammer/fuzzysearch v1.1.8 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/prometheus/common/sigv4 v0.1.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect
go.opentelemetry.io/otel/metric v1.21.0 // indirect
google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe // indirect
gopkg.in/evanphx/json-patch.v5 v5.7.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
require (
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/BurntSushi/toml v1.2.1 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/Masterminds/semver/v3 v3.2.0 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/Masterminds/squirrel v1.5.4 // indirect
github.com/aquasecurity/defsec v0.93.1 // indirect
github.com/aquasecurity/go-dep-parser v0.0.0-20231030050624-4548cca9a5c9 // indirect
github.com/Masterminds/squirrel v1.5.3 // indirect
github.com/aquasecurity/defsec v0.86.0 // indirect
github.com/aquasecurity/go-dep-parser v0.0.0-20230413091456-df0396537e15 // indirect
github.com/aquasecurity/table v1.8.0 // indirect
github.com/aquasecurity/tml v0.6.1 // indirect
github.com/aquasecurity/trivy v0.47.0 // indirect
github.com/aquasecurity/trivy-db v0.0.0-20231020043206-3770774790ce // indirect
github.com/aquasecurity/trivy v0.40.0 // indirect
github.com/aquasecurity/trivy-db v0.0.0-20230411140759-3c2ee2168575 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/containerd/containerd v1.7.11 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/cli v24.0.7+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v24.0.7+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.0 // indirect
github.com/containerd/containerd v1.7.0 // indirect
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/cli v23.0.1+incompatible // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/docker v23.0.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/evanphx/json-patch v5.7.0+incompatible // indirect
github.com/emicklei/go-restful/v3 v3.10.2 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
github.com/go-logr/logr v1.3.0 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.20.0 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.4 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.3 // 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-containerregistry v0.16.1 // indirect
github.com/google/gnostic v0.6.9 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/go-containerregistry v0.14.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.5.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/gosuri/uitable v0.0.4 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
@@ -154,21 +80,22 @@ require (
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/huandu/xstrings v1.4.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/imdario/mergo v0.3.15 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jmoiron/sqlx v1.3.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.2 // indirect
github.com/klauspost/compress v1.16.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
github.com/lib/pq v1.10.7 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/masahiro331/go-xfs-filesystem v0.0.0-20230608043311-a335f4599b70 // indirect
github.com/masahiro331/go-xfs-filesystem v0.0.0-20221225060805-c02764233454 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
@@ -176,72 +103,81 @@ require (
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/spdystream v0.2.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221020182949-4df8887994e8 // indirect
github.com/pelletier/go-toml/v2 v2.0.7 // indirect
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.18.0
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.15.0
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/robfig/cron/v3 v3.0.1
github.com/rubenv/sql-migrate v1.5.2 // indirect
github.com/rubenv/sql-migrate v1.3.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/samber/lo v1.38.1 // indirect
github.com/samber/lo v1.37.0 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spdx/tools-golang v0.5.3 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/spdx/tools-golang v0.5.0 // indirect
github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/subosito/gotenv v1.4.2 // 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.21.0 // indirect
go.opentelemetry.io/otel/trace v1.21.0 // indirect
go.starlark.net v0.0.0-20231016134836-22325403fcb3 // indirect
github.com/xlab/treeprint v1.1.0 // indirect
go.opentelemetry.io/otel v1.14.0 // indirect
go.opentelemetry.io/otel/trace v1.14.0 // indirect
go.starlark.net v0.0.0-20221020143700-22309ac47eac // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0
golang.org/x/crypto v0.18.0 // indirect
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
golang.org/x/net v0.20.0
golang.org/x/oauth2 v0.16.0 // indirect
golang.org/x/sync v0.5.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/grpc v1.61.0
google.golang.org/protobuf v1.32.0 // indirect
go.uber.org/zap v1.24.0
golang.org/x/crypto v0.7.0 // indirect
golang.org/x/exp v0.0.0-20230124195608-d38c7dcee874 // indirect
golang.org/x/net v0.9.0 // indirect
golang.org/x/oauth2 v0.6.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect
google.golang.org/grpc v1.54.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
k8s.io/apiextensions-apiserver v0.28.4
k8s.io/apiserver v0.28.4 // indirect
k8s.io/cli-runtime v0.28.4 // indirect
k8s.io/component-base v0.28.4 // indirect
k8s.io/klog/v2 v2.100.1 // indirect
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
oras.land/oras-go v1.2.4 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.26.3 // indirect
k8s.io/apiserver v0.26.3 // indirect
k8s.io/cli-runtime v0.26.3 // indirect
k8s.io/component-base v0.26.3 // indirect
k8s.io/klog/v2 v2.90.1 // indirect
k8s.io/kube-openapi v0.0.0-20230327201221-f5883ff37f0c // indirect
k8s.io/utils v0.0.0-20230313181309-38a27ef9d749 // indirect
oras.land/oras-go v1.2.2 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/kustomize/api v0.15.0 // indirect
sigs.k8s.io/kustomize/kyaml v0.15.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.0 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
sigs.k8s.io/kustomize/api v0.12.1 // indirect
sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
// v1.2.0 is taken from github.com/open-policy-agent/opa v0.42.0
// v1.2.0 incompatible with github.com/docker/docker v23.0.0-rc.1+incompatible
replace oras.land/oras-go => oras.land/oras-go v1.2.4
replace oras.land/oras-go => oras.land/oras-go v1.2.3
// v0.3.1-0.20230104082527-d6f58551be3f is taken from github.com/moby/buildkit v0.11.0
// spdx logic write on v0.3.0 and incompatible with v0.3.1-0.20230104082527-d6f58551be3f
replace github.com/spdx/tools-golang => github.com/spdx/tools-golang v0.3.0

2793
go.sum

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 235 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -15,12 +15,8 @@ package main
import "github.com/k8sgpt-ai/k8sgpt/cmd"
var (
version = "dev"
commit = "HEAD"
date = "unknown"
)
var version = "dev"
func main() {
cmd.Execute(version, commit, date)
cmd.Execute(version)
}

View File

@@ -1,153 +0,0 @@
package ai
import (
"context"
"encoding/json"
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/bedrockruntime"
)
const amazonbedrockAIClientName = "amazonbedrock"
// AmazonBedRockClient represents the client for interacting with the Amazon Bedrock service.
type AmazonBedRockClient struct {
nopCloser
client *bedrockruntime.BedrockRuntime
model string
temperature float32
}
// InvokeModelResponseBody represents the response body structure from the model invocation.
type InvokeModelResponseBody struct {
Completion string `json:"completion"`
Stop_reason string `json:"stop_reason"`
}
// Amazon BedRock support region list US East (N. Virginia),US West (Oregon),Asia Pacific (Singapore),Asia Pacific (Tokyo),Europe (Frankfurt)
// https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html#bedrock-regions
const BEDROCK_DEFAULT_REGION = "us-east-1" // default use us-east-1 region
const (
US_East_1 = "us-east-1"
US_West_2 = "us-west-2"
AP_Southeast_1 = "ap-southeast-1"
AP_Northeast_1 = "ap-northeast-1"
EU_Central_1 = "eu-central-1"
)
var BEDROCKER_SUPPORTED_REGION = []string{
US_East_1,
US_West_2,
AP_Southeast_1,
AP_Northeast_1,
EU_Central_1,
}
const (
ModelAnthropicClaudeV2 = "anthropic.claude-v2"
ModelAnthropicClaudeV1 = "anthropic.claude-v1"
ModelAnthropicClaudeInstantV1 = "anthropic.claude-instant-v1"
)
var BEDROCK_MODELS = []string{
ModelAnthropicClaudeV2,
ModelAnthropicClaudeV1,
ModelAnthropicClaudeInstantV1,
}
// GetModelOrDefault check config model
func GetModelOrDefault(model string) string {
// Check if the provided model is in the list
for _, m := range BEDROCK_MODELS {
if m == model {
return model // Return the provided model
}
}
// Return the default model if the provided model is not in the list
return BEDROCK_MODELS[0]
}
// GetModelOrDefault check config region
func GetRegionOrDefault(region string) string {
// Check if the provided model is in the list
for _, m := range BEDROCKER_SUPPORTED_REGION {
if m == region {
return region // Return the provided model
}
}
// Return the default model if the provided model is not in the list
return BEDROCK_DEFAULT_REGION
}
// Configure configures the AmazonBedRockClient with the provided configuration.
func (a *AmazonBedRockClient) Configure(config IAIConfig) error {
// Create a new AWS session
providerRegion := GetRegionOrDefault(config.GetProviderRegion())
sess, err := session.NewSession(&aws.Config{
Region: aws.String(providerRegion),
})
if err != nil {
return err
}
// Create a new BedrockRuntime client
a.client = bedrockruntime.New(sess)
a.model = GetModelOrDefault(config.GetModel())
a.temperature = config.GetTemperature()
return nil
}
// GetCompletion sends a request to the model for generating completion based on the provided prompt.
func (a *AmazonBedRockClient) GetCompletion(ctx context.Context, prompt string) (string, error) {
// Prepare the input data for the model invocation
request := map[string]interface{}{
"prompt": fmt.Sprintf("\n\nHuman: %s \n\nAssistant:", prompt),
"max_tokens_to_sample": 1024,
"temperature": a.temperature,
"top_p": 0.9,
}
body, err := json.Marshal(request)
if err != nil {
return "", err
}
// Build the parameters for the model invocation
params := &bedrockruntime.InvokeModelInput{
Body: body,
ModelId: aws.String(a.model),
ContentType: aws.String("application/json"),
Accept: aws.String("application/json"),
}
// Invoke the model
resp, err := a.client.InvokeModelWithContext(ctx, params)
if err != nil {
return "", err
}
// Parse the response body
output := &InvokeModelResponseBody{}
err = json.Unmarshal(resp.Body, output)
if err != nil {
return "", err
}
return output.Completion, nil
}
// GetName returns the name of the AmazonBedRockClient.
func (a *AmazonBedRockClient) GetName() string {
return amazonbedrockAIClientName
}

View File

@@ -1,137 +0,0 @@
/*
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"
"encoding/json"
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/sagemakerruntime"
)
const amazonsagemakerAIClientName = "amazonsagemaker"
type SageMakerAIClient struct {
nopCloser
client *sagemakerruntime.SageMakerRuntime
model string
temperature float32
endpoint string
topP float32
maxTokens int
}
type Generations []struct {
Generation struct {
Role string `json:"role"`
Content string `json:"content"`
} `json:"generation"`
}
type Request struct {
Inputs [][]Message `json:"inputs"`
Parameters Parameters `json:"parameters"`
}
type Message struct {
Role string `json:"role"`
Content string `json:"content"`
}
type Parameters struct {
MaxNewTokens int `json:"max_new_tokens"`
TopP float64 `json:"top_p"`
Temperature float64 `json:"temperature"`
}
func (c *SageMakerAIClient) Configure(config IAIConfig) error {
// Create a new AWS session
sess := session.Must(session.NewSessionWithOptions(session.Options{
Config: aws.Config{Region: aws.String(config.GetProviderRegion())},
SharedConfigState: session.SharedConfigEnable,
}))
// Create a new SageMaker runtime client
c.client = sagemakerruntime.New(sess)
c.model = config.GetModel()
c.endpoint = config.GetEndpointName()
c.temperature = config.GetTemperature()
c.maxTokens = config.GetMaxTokens()
c.topP = config.GetTopP()
return nil
}
func (c *SageMakerAIClient) GetCompletion(_ context.Context, prompt string) (string, error) {
// Create a completion request
request := Request{
Inputs: [][]Message{
{
{Role: "system", Content: "DEFAULT_PROMPT"},
{Role: "user", Content: prompt},
},
},
Parameters: Parameters{
MaxNewTokens: int(c.maxTokens),
TopP: float64(c.topP),
Temperature: float64(c.temperature),
},
}
// Convert request to []byte
bytesData, err := json.Marshal(request)
if err != nil {
return "", err
}
// Create an input object
input := &sagemakerruntime.InvokeEndpointInput{
Body: bytesData,
EndpointName: aws.String(c.endpoint),
ContentType: aws.String("application/json"), // Set the content type as per your model's requirements
Accept: aws.String("application/json"), // Set the accept type as per your model's requirements
CustomAttributes: aws.String("accept_eula=true"),
}
// Call the InvokeEndpoint function
result, err := c.client.InvokeEndpoint(input)
if err != nil {
return "", err
}
// // Define a slice of Generations
var generations Generations
err = json.Unmarshal([]byte(string(result.Body)), &generations)
if err != nil {
return "", err
}
// Check for length of generations
if len(generations) != 1 {
return "", fmt.Errorf("Expected exactly one generation, but got %d", len(generations))
}
// Access the content
content := generations[0].Generation.Content
return content, nil
}
func (c *SageMakerAIClient) GetName() string {
return amazonsagemakerAIClientName
}

View File

@@ -1,64 +0,0 @@
package ai
import (
"context"
"errors"
"github.com/sashabaranov/go-openai"
)
const azureAIClientName = "azureopenai"
type AzureAIClient struct {
nopCloser
client *openai.Client
model string
temperature float32
}
func (c *AzureAIClient) Configure(config IAIConfig) error {
token := config.GetPassword()
baseURL := config.GetBaseURL()
engine := config.GetEngine()
defaultConfig := openai.DefaultAzureConfig(token, baseURL)
defaultConfig.AzureModelMapperFunc = func(model string) string {
// If you use a deployment name different from the model name, you can customize the AzureModelMapperFunc function
azureModelMapping := map[string]string{
model: engine,
}
return azureModelMapping[model]
}
client := openai.NewClientWithConfig(defaultConfig)
if client == nil {
return errors.New("error creating Azure OpenAI client")
}
c.client = client
c.model = config.GetModel()
c.temperature = config.GetTemperature()
return nil
}
func (c *AzureAIClient) GetCompletion(ctx context.Context, prompt string) (string, error) {
// Create a completion request
resp, err := c.client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{
Model: c.model,
Messages: []openai.ChatCompletionMessage{
{
Role: openai.ChatMessageRoleUser,
Content: prompt,
},
},
Temperature: c.temperature,
})
if err != nil {
return "", err
}
return resp.Choices[0].Message.Content, nil
}
func (c *AzureAIClient) GetName() string {
return azureAIClientName
}

View File

@@ -1,74 +0,0 @@
/*
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"
"github.com/cohere-ai/cohere-go"
)
const cohereAIClientName = "cohere"
type CohereClient struct {
nopCloser
client *cohere.Client
model string
temperature float32
}
func (c *CohereClient) Configure(config IAIConfig) error {
token := config.GetPassword()
client, err := cohere.CreateClient(token)
if err != nil {
return err
}
baseURL := config.GetBaseURL()
if baseURL != "" {
client.BaseURL = baseURL
}
if client == nil {
return errors.New("error creating Cohere client")
}
c.client = client
c.model = config.GetModel()
c.temperature = config.GetTemperature()
return nil
}
func (c *CohereClient) GetCompletion(_ context.Context, prompt string) (string, error) {
// Create a completion request
resp, err := c.client.Generate(cohere.GenerateOptions{
Model: c.model,
Prompt: prompt,
MaxTokens: cohere.Uint(2048),
Temperature: cohere.Float64(float64(c.temperature)),
K: cohere.Int(0),
StopSequences: []string{},
ReturnLikelihoods: "NONE",
})
if err != nil {
return "", err
}
return resp.Generations[0].Text, nil
}
func (c *CohereClient) GetName() string {
return cohereAIClientName
}

View File

@@ -1,119 +0,0 @@
/*
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"
"fmt"
"github.com/fatih/color"
"github.com/google/generative-ai-go/genai"
"google.golang.org/api/option"
)
const googleAIClientName = "google"
type GoogleGenAIClient struct {
client *genai.Client
model string
temperature float32
topP float32
maxTokens int
}
func (c *GoogleGenAIClient) Configure(config IAIConfig) error {
ctx := context.Background()
// Access your API key as an environment variable (see "Set up your API key" above)
token := config.GetPassword()
authOption := option.WithAPIKey(token)
if token[0] == '{' {
authOption = option.WithCredentialsJSON([]byte(token))
}
client, err := genai.NewClient(ctx, authOption)
if err != nil {
return fmt.Errorf("creating genai Google SDK client: %w", err)
}
c.client = client
c.model = config.GetModel()
c.temperature = config.GetTemperature()
c.topP = config.GetTopP()
c.maxTokens = config.GetMaxTokens()
return nil
}
func (c *GoogleGenAIClient) GetCompletion(ctx context.Context, prompt string) (string, error) {
// Available models are at https://ai.google.dev/models e.g.gemini-pro.
model := c.client.GenerativeModel(c.model)
model.SetTemperature(c.temperature)
model.SetTopP(c.topP)
model.SetMaxOutputTokens(int32(c.maxTokens))
// Google AI SDK is capable of different inputs than just text, for now set explicit text prompt type.
// Similarly, we could stream the response. For now k8sgpt does not support streaming.
resp, err := model.GenerateContent(ctx, genai.Text(prompt))
if err != nil {
return "", err
}
if len(resp.Candidates) == 0 {
if resp.PromptFeedback.BlockReason == genai.BlockReasonSafety {
for _, r := range resp.PromptFeedback.SafetyRatings {
if !r.Blocked {
continue
}
return "", fmt.Errorf("complection blocked due to %v with probability %v", r.Category.String(), r.Probability.String())
}
}
return "", errors.New("no complection returned; unknown reason")
}
// Format output.
// TODO(bwplotka): Provider richer output in certain cases e.g. suddenly finished
// completion based on finish reasons or safety rankings.
got := resp.Candidates[0]
var output string
for _, part := range got.Content.Parts {
switch o := part.(type) {
case genai.Text:
output += string(o)
output += "\n"
default:
color.Yellow("found unsupported AI response part of type %T; ignoring", part)
}
}
if got.CitationMetadata != nil && len(got.CitationMetadata.CitationSources) > 0 {
output += "Citations:\n"
for _, source := range got.CitationMetadata.CitationSources {
// TODO(bwplotka): Give details around what exactly words could be attributed to the citation.
output += fmt.Sprintf("* %s, %s\n", *source.URI, source.License)
}
}
return output, nil
}
func (c *GoogleGenAIClient) GetName() string {
return googleAIClientName
}
func (c *GoogleGenAIClient) Close() {
if err := c.client.Close(); err != nil {
color.Red("googleai client close error: %v", err)
}
}

View File

@@ -15,105 +15,51 @@ package ai
import (
"context"
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
)
var (
clients = []IAI{
&OpenAIClient{},
&AzureAIClient{},
&LocalAIClient{},
&NoOpAIClient{},
&CohereClient{},
&AmazonBedRockClient{},
&SageMakerAIClient{},
&GoogleGenAIClient{},
}
Backends = []string{
openAIClientName,
localAIClientName,
azureAIClientName,
cohereAIClientName,
amazonbedrockAIClientName,
amazonsagemakerAIClientName,
googleAIClientName,
noopAIClientName,
}
)
// IAI is an interface all clients (representing backends) share.
type IAI interface {
// Configure sets up client for given configuration. This is expected to be
// executed once per client life-time (e.g. analysis CLI command invocation).
Configure(config IAIConfig) error
// GetCompletion generates text based on prompt.
Configure(config IAIConfig, language string) error
GetCompletion(ctx context.Context, prompt string) (string, error)
// GetName returns name of the backend/client.
Parse(ctx context.Context, prompt []string, cache cache.ICache) (string, error)
GetName() string
// Close cleans all the resources. No other methods should be used on the
// objects after this method is invoked.
Close()
}
type nopCloser struct{}
func (nopCloser) Close() {}
type IAIConfig interface {
GetPassword() string
GetModel() string
GetBaseURL() string
GetEndpointName() string
GetEngine() string
GetTemperature() float32
GetProviderRegion() string
GetTopP() float32
GetMaxTokens() int
}
func NewClient(provider string) IAI {
for _, c := range clients {
if provider == c.GetName() {
return c
}
switch provider {
case "openai":
return &OpenAIClient{}
case "localai":
return &LocalAIClient{}
case "noopai":
return &NoOpAIClient{}
default:
return &OpenAIClient{}
}
// default client
return &OpenAIClient{}
}
type AIConfiguration struct {
Providers []AIProvider `mapstructure:"providers"`
DefaultProvider string `mapstructure:"defaultprovider"`
Providers []AIProvider `mapstructure:"providers"`
}
type AIProvider struct {
Name string `mapstructure:"name"`
Model string `mapstructure:"model"`
Password string `mapstructure:"password" yaml:"password,omitempty"`
BaseURL string `mapstructure:"baseurl" yaml:"baseurl,omitempty"`
EndpointName string `mapstructure:"endpointname" yaml:"endpointname,omitempty"`
Engine string `mapstructure:"engine" yaml:"engine,omitempty"`
Temperature float32 `mapstructure:"temperature" yaml:"temperature,omitempty"`
ProviderRegion string `mapstructure:"providerregion" yaml:"providerregion,omitempty"`
TopP float32 `mapstructure:"topp" yaml:"topp,omitempty"`
MaxTokens int `mapstructure:"maxtokens" yaml:"maxtokens,omitempty"`
Name string `mapstructure:"name"`
Model string `mapstructure:"model"`
Password string `mapstructure:"password"`
BaseURL string `mapstructure:"baseurl"`
}
func (p *AIProvider) GetBaseURL() string {
return p.BaseURL
}
func (p *AIProvider) GetEndpointName() string {
return p.EndpointName
}
func (p *AIProvider) GetTopP() float32 {
return p.TopP
}
func (p *AIProvider) GetMaxTokens() int {
return p.MaxTokens
}
func (p *AIProvider) GetPassword() string {
return p.Password
}
@@ -122,24 +68,6 @@ func (p *AIProvider) GetModel() string {
return p.Model
}
func (p *AIProvider) GetEngine() string {
return p.Engine
}
func (p *AIProvider) GetTemperature() float32 {
return p.Temperature
}
func (p *AIProvider) GetProviderRegion() string {
return p.ProviderRegion
}
var passwordlessProviders = []string{"localai", "amazonsagemaker", "amazonbedrock"}
func NeedPassword(backend string) bool {
for _, b := range passwordlessProviders {
if b == backend {
return false
}
}
return true
return backend != "localai"
}

View File

@@ -1,67 +0,0 @@
package interactive
import (
"fmt"
"strings"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/analysis"
"github.com/pterm/pterm"
)
type INTERACTIVE_STATE int
const (
prompt = "Given the following context: "
)
const (
E_RUNNING INTERACTIVE_STATE = iota
E_EXITED = iota
)
type InteractionRunner struct {
config *analysis.Analysis
State chan INTERACTIVE_STATE
contextWindow []byte
}
func NewInteractionRunner(config *analysis.Analysis, contextWindow []byte) *InteractionRunner {
return &InteractionRunner{
config: config,
contextWindow: contextWindow,
State: make(chan INTERACTIVE_STATE),
}
}
func (a *InteractionRunner) StartInteraction() {
a.State <- E_RUNNING
pterm.Println("Interactive mode enabled [type exit to close.]")
for {
query := pterm.DefaultInteractiveTextInput.WithMultiLine(false)
queryString, err := query.Show()
if err != nil {
fmt.Println(err)
}
if queryString == "" {
continue
}
if strings.Contains(queryString, "exit") {
a.State <- E_EXITED
continue
}
pterm.Println()
contextWindow := fmt.Sprintf("%s %s %s", prompt, string(a.contextWindow),
queryString)
response, err := a.config.AIClient.GetCompletion(a.config.Context,
contextWindow)
if err != nil {
color.Red("Error: %v", err)
a.State <- E_EXITED
continue
}
pterm.Println(response)
}
}

View File

@@ -1,11 +1,9 @@
package ai
const localAIClientName = "localai"
type LocalAIClient struct {
OpenAIClient
}
func (a *LocalAIClient) GetName() string {
return localAIClientName
return "localai"
}

View File

@@ -15,23 +15,58 @@ package ai
import (
"context"
"encoding/base64"
"fmt"
"strings"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
)
const noopAIClientName = "noopai"
type NoOpAIClient struct {
nopCloser
client string
language string
model string
}
func (c *NoOpAIClient) Configure(_ IAIConfig) error {
func (c *NoOpAIClient) Configure(config IAIConfig, language string) error {
token := config.GetPassword()
c.language = language
c.client = fmt.Sprintf("I am a noop client with the token %s ", token)
c.model = config.GetModel()
return nil
}
func (c *NoOpAIClient) GetCompletion(_ context.Context, prompt string) (string, error) {
func (c *NoOpAIClient) GetCompletion(ctx context.Context, prompt string) (string, error) {
// Create a completion request
response := "I am a noop response to the prompt " + prompt
return response, nil
}
func (c *NoOpAIClient) GetName() string {
return noopAIClientName
func (a *NoOpAIClient) Parse(ctx context.Context, prompt []string, cache cache.ICache) (string, error) {
// parse the text with the AI backend
inputKey := strings.Join(prompt, " ")
// Check for cached data
sEnc := base64.StdEncoding.EncodeToString([]byte(inputKey))
cacheKey := util.GetCacheKey(a.GetName(), a.language, sEnc)
response, err := a.GetCompletion(ctx, inputKey)
if err != nil {
color.Red("error getting completion: %v", err)
return "", err
}
err = cache.Store(cacheKey, base64.StdEncoding.EncodeToString([]byte(response)))
if err != nil {
color.Red("error storing value to cache: %v", err)
return "", nil
}
return response, nil
}
func (a *NoOpAIClient) GetName() string {
return "noopai"
}

View File

@@ -15,30 +15,26 @@ package ai
import (
"context"
"encoding/base64"
"errors"
"fmt"
"strings"
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
"github.com/sashabaranov/go-openai"
)
const openAIClientName = "openai"
"github.com/fatih/color"
)
type OpenAIClient struct {
nopCloser
client *openai.Client
model string
temperature float32
client *openai.Client
language string
model string
}
const (
// OpenAI completion parameters
maxToken = 2048
presencePenalty = 0.0
frequencyPenalty = 0.0
topP = 1.0
)
func (c *OpenAIClient) Configure(config IAIConfig) error {
func (c *OpenAIClient) Configure(config IAIConfig, language string) error {
token := config.GetPassword()
defaultConfig := openai.DefaultConfig(token)
@@ -51,9 +47,9 @@ func (c *OpenAIClient) Configure(config IAIConfig) error {
if client == nil {
return errors.New("error creating OpenAI client")
}
c.language = language
c.client = client
c.model = config.GetModel()
c.temperature = config.GetTemperature()
return nil
}
@@ -64,14 +60,9 @@ func (c *OpenAIClient) GetCompletion(ctx context.Context, prompt string) (string
Messages: []openai.ChatCompletionMessage{
{
Role: "user",
Content: prompt,
Content: fmt.Sprintf(default_prompt, c.language, prompt),
},
},
Temperature: c.temperature,
MaxTokens: maxToken,
PresencePenalty: presencePenalty,
FrequencyPenalty: frequencyPenalty,
TopP: topP,
})
if err != nil {
return "", err
@@ -79,6 +70,42 @@ func (c *OpenAIClient) GetCompletion(ctx context.Context, prompt string) (string
return resp.Choices[0].Message.Content, nil
}
func (c *OpenAIClient) GetName() string {
return openAIClientName
func (a *OpenAIClient) Parse(ctx context.Context, prompt []string, cache cache.ICache) (string, error) {
inputKey := strings.Join(prompt, " ")
// Check for cached data
cacheKey := util.GetCacheKey(a.GetName(), a.language, inputKey)
if !cache.IsCacheDisabled() && cache.Exists(cacheKey) {
response, err := cache.Load(cacheKey)
if err != nil {
return "", err
}
if response != "" {
output, err := base64.StdEncoding.DecodeString(response)
if err != nil {
color.Red("error decoding cached data: %v", err)
return "", nil
}
return string(output), nil
}
}
response, err := a.GetCompletion(ctx, inputKey)
if err != nil {
return "", err
}
err = cache.Store(cacheKey, base64.StdEncoding.EncodeToString([]byte(response)))
if err != nil {
color.Red("error storing value to cache: %v", err)
return "", nil
}
return response, nil
}
func (a *OpenAIClient) GetName() string {
return "openai"
}

View File

@@ -1,59 +1,5 @@
package ai
const (
default_prompt = `Simplify the following Kubernetes error message delimited by triple dashes written in --- %s --- language; --- %s ---.
Provide the most possible solution in a step by step style in no more than 280 characters. Write the output in the following format:
Error: {Explain error here}
Solution: {Step by step solution here}
`
trivy_vuln_prompt = "Explain the following trivy scan result and the detail risk or root cause of the CVE ID, then provide a solution. Response in %s: %s"
trivy_conf_prompt = "Explain the following trivy scan result and the detail risk or root cause of the security check, then provide a solution."
prom_conf_prompt = `Simplify the following Prometheus error message delimited by triple dashes written in --- %s --- language; --- %s ---.
This error came when validating the Prometheus configuration file.
Provide step by step instructions to fix, with suggestions, referencing Prometheus documentation if relevant.
Write the output in the following format in no more than 300 characters:
Error: {Explain error here}
Solution: {Step by step solution here}
`
prom_relabel_prompt = `
Return your prompt in this language: %s, beginning with
The following is a list of the form:
job_name:
{Prometheus job_name}
relabel_configs:
{Prometheus relabel_configs}
kubernetes_sd_configs:
{Prometheus service discovery config}
---
%s
---
For each job_name, describe the Kubernetes service and pod labels,
namespaces, ports, and containers they match.
Return the message:
Discovered and parsed Prometheus scrape configurations.
For targets to be scraped by Prometheus, ensure they are running with
at least one of the following label sets:
Then for each job, write this format:
- Job: {job_name}
- Service Labels:
- {list of service labels}
- Pod Labels:
- {list of pod labels}
- Namespaces:
- {list of namespaces}
- Ports:
- {list of ports}
- Containers:
- {list of container names}
`
default_prompt = "Simplify the following Kubernetes error message and provide a solution in %s: %s"
)
var PromptMap = map[string]string{
"default": default_prompt,
"VulnerabilityReport": trivy_vuln_prompt, // for Trivy integration, the key should match `Result.Kind` in pkg/common/types.go
"ConfigAuditReport": trivy_conf_prompt,
"PrometheusConfigValidate": prom_conf_prompt,
"PrometheusConfigRelabelReport": prom_relabel_prompt,
}

View File

@@ -15,15 +15,14 @@ package analysis
import (
"context"
"encoding/base64"
"errors"
"fmt"
"os"
"reflect"
"strings"
"sync"
"github.com/fatih/color"
openapi_v2 "github.com/google/gnostic/openapiv2"
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer"
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
@@ -35,23 +34,18 @@ import (
)
type Analysis struct {
Context context.Context
Filters []string
Client *kubernetes.Client
Language string
AIClient ai.IAI
Results []common.Result
Errors []string
Namespace string
Cache cache.ICache
Explain bool
MaxConcurrency int
AnalysisAIProvider string // The name of the AI Provider used for this analysis
WithDoc bool
Context context.Context
Filters []string
Client *kubernetes.Client
AIClient ai.IAI
Results []common.Result
Namespace string
Cache cache.ICache
Explain bool
MaxConcurrency int
}
type AnalysisStatus string
type AnalysisErrors []string
const (
StateOK AnalysisStatus = "OK"
@@ -59,71 +53,22 @@ const (
)
type JsonOutput struct {
Provider string `json:"provider"`
Errors AnalysisErrors `json:"errors"`
Status AnalysisStatus `json:"status"`
Problems int `json:"problems"`
Results []common.Result `json:"results"`
}
func NewAnalysis(
backend string,
language string,
filters []string,
namespace string,
noCache bool,
explain bool,
maxConcurrency int,
withDoc bool,
interactiveMode bool,
) (*Analysis, error) {
// Get kubernetes client from viper.
kubecontext := viper.GetString("kubecontext")
kubeconfig := viper.GetString("kubeconfig")
client, err := kubernetes.NewClient(kubecontext, kubeconfig)
if err != nil {
return nil, fmt.Errorf("initialising kubernetes client: %w", err)
}
// Load remote cache if it is configured.
cache, err := cache.GetCacheConfiguration()
if err != nil {
return nil, err
}
if noCache {
cache.DisableCache()
}
a := &Analysis{
Context: context.Background(),
Filters: filters,
Client: client,
Language: language,
Namespace: namespace,
Cache: cache,
Explain: explain,
MaxConcurrency: maxConcurrency,
WithDoc: withDoc,
}
if !explain {
// Return early if AI use was not requested.
return a, nil
}
func NewAnalysis(backend string, language string, filters []string, namespace string, noCache bool, explain bool, maxConcurrency int) (*Analysis, error) {
var configAI ai.AIConfiguration
if err := viper.UnmarshalKey("ai", &configAI); err != nil {
return nil, err
err := viper.UnmarshalKey("ai", &configAI)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
if len(configAI.Providers) == 0 {
return nil, errors.New("AI provider not specified in configuration. Please run k8sgpt auth")
}
// Backend string will have high priority than a default provider
// Backend as "openai" represents the default CLI argument passed through
if configAI.DefaultProvider != "" && backend == "openai" {
backend = configAI.DefaultProvider
if len(configAI.Providers) == 0 && explain {
color.Red("Error: AI provider not specified in configuration. Please run k8sgpt auth")
os.Exit(1)
}
var aiProvider ai.AIProvider
@@ -135,48 +80,58 @@ func NewAnalysis(
}
if aiProvider.Name == "" {
return nil, fmt.Errorf("AI provider %s not specified in configuration. Please run k8sgpt auth", backend)
color.Red("Error: AI provider %s not specified in configuration. Please run k8sgpt auth", backend)
return nil, errors.New("AI provider not specified in configuration")
}
aiClient := ai.NewClient(aiProvider.Name)
if err := aiClient.Configure(&aiProvider); err != nil {
if err := aiClient.Configure(&aiProvider, language); err != nil {
color.Red("Error: %v", err)
return nil, err
}
a.AIClient = aiClient
a.AnalysisAIProvider = aiProvider.Name
return a, nil
ctx := context.Background()
// Get kubernetes client from viper
kubecontext := viper.GetString("kubecontext")
kubeconfig := viper.GetString("kubeconfig")
client, err := kubernetes.NewClient(kubecontext, kubeconfig)
if err != nil {
color.Red("Error initialising kubernetes client: %v", err)
return nil, err
}
return &Analysis{
Context: ctx,
Filters: filters,
Client: client,
AIClient: aiClient,
Namespace: namespace,
Cache: cache.New(noCache),
Explain: explain,
MaxConcurrency: maxConcurrency,
}, nil
}
func (a *Analysis) RunAnalysis() {
func (a *Analysis) RunAnalysis() []error {
activeFilters := viper.GetStringSlice("active_filters")
coreAnalyzerMap, analyzerMap := analyzer.GetAnalyzerMap()
// we get the openapi schema from the server only if required by the flag "with-doc"
openapiSchema := &openapi_v2.Document{}
if a.WithDoc {
var openApiErr error
openapiSchema, openApiErr = a.Client.Client.Discovery().OpenAPISchema()
if openApiErr != nil {
a.Errors = append(a.Errors, fmt.Sprintf("[KubernetesDoc] %s", openApiErr))
}
}
analyzerMap := analyzer.GetAnalyzerMap()
analyzerConfig := common.Analyzer{
Client: a.Client,
Context: a.Context,
Namespace: a.Namespace,
AIClient: a.AIClient,
OpenapiSchema: openapiSchema,
Client: a.Client,
Context: a.Context,
Namespace: a.Namespace,
AIClient: a.AIClient,
}
var errorList []error
semaphore := make(chan struct{}, a.MaxConcurrency)
// if there are no filters selected and no active_filters then run coreAnalyzer
// if there are no filters selected and no active_filters then run all of them
if len(a.Filters) == 0 && len(activeFilters) == 0 {
var wg sync.WaitGroup
var mutex sync.Mutex
for _, analyzer := range coreAnalyzerMap {
for _, analyzer := range analyzerMap {
wg.Add(1)
semaphore <- struct{}{}
go func(analyzer common.IAnalyzer, wg *sync.WaitGroup, semaphore chan struct{}) {
@@ -184,7 +139,7 @@ func (a *Analysis) RunAnalysis() {
results, err := analyzer.Analyze(analyzerConfig)
if err != nil {
mutex.Lock()
a.Errors = append(a.Errors, fmt.Sprintf("[%s] %s", reflect.TypeOf(analyzer).Name(), err))
errorList = append(errorList, fmt.Errorf(fmt.Sprintf("[%s] %s", reflect.TypeOf(analyzer).Name(), err)))
mutex.Unlock()
}
mutex.Lock()
@@ -195,7 +150,7 @@ func (a *Analysis) RunAnalysis() {
}
wg.Wait()
return
return errorList
}
semaphore = make(chan struct{}, a.MaxConcurrency)
// if the filters flag is specified
@@ -211,7 +166,7 @@ func (a *Analysis) RunAnalysis() {
results, err := analyzer.Analyze(analyzerConfig)
if err != nil {
mutex.Lock()
a.Errors = append(a.Errors, fmt.Sprintf("[%s] %s", filter, err))
errorList = append(errorList, fmt.Errorf(fmt.Sprintf("[%s] %s", filter, err)))
mutex.Unlock()
}
mutex.Lock()
@@ -220,11 +175,11 @@ func (a *Analysis) RunAnalysis() {
<-semaphore
}(analyzer, filter)
} else {
a.Errors = append(a.Errors, fmt.Sprintf("\"%s\" filter does not exist. Please run k8sgpt filters list.", filter))
errorList = append(errorList, fmt.Errorf(fmt.Sprintf("\"%s\" filter does not exist. Please run k8sgpt filters list.", filter)))
}
}
wg.Wait()
return
return errorList
}
var wg sync.WaitGroup
@@ -240,7 +195,7 @@ func (a *Analysis) RunAnalysis() {
results, err := analyzer.Analyze(analyzerConfig)
if err != nil {
mutex.Lock()
a.Errors = append(a.Errors, fmt.Sprintf("[%s] %s", filter, err))
errorList = append(errorList, fmt.Errorf("[%s] %s", filter, err))
mutex.Unlock()
}
mutex.Lock()
@@ -251,6 +206,7 @@ func (a *Analysis) RunAnalysis() {
}
}
wg.Wait()
return errorList
}
func (a *Analysis) GetAIResults(output string, anonymize bool) error {
@@ -274,82 +230,35 @@ func (a *Analysis) GetAIResults(output string, anonymize bool) error {
}
texts = append(texts, failure.Text)
}
promptTemplate := ai.PromptMap["default"]
// If the resource `Kind` comes from an "integration plugin",
// maybe a customized prompt template will be involved.
if prompt, ok := ai.PromptMap[analysis.Kind]; ok {
promptTemplate = prompt
}
result, err := a.getAIResultForSanitizedFailures(texts, promptTemplate)
parsedText, err := a.AIClient.Parse(a.Context, texts, a.Cache)
if err != nil {
// FIXME: can we avoid checking if output is json multiple times?
// maybe implement the progress bar better?
if output != "json" {
_ = bar.Exit()
bar.Exit()
}
// Check for exhaustion.
// Check for exhaustion
if strings.Contains(err.Error(), "status code: 429") {
return fmt.Errorf("exhausted API quota for AI provider %s: %v", a.AIClient.GetName(), err)
} else {
return fmt.Errorf("failed while calling AI provider %s: %v", a.AIClient.GetName(), err)
}
return fmt.Errorf("failed while calling AI provider %s: %v", a.AIClient.GetName(), err)
}
if anonymize {
for _, failure := range analysis.Error {
for _, s := range failure.Sensitive {
result = strings.ReplaceAll(result, s.Masked, s.Unmasked)
parsedText = strings.ReplaceAll(parsedText, s.Masked, s.Unmasked)
}
}
}
analysis.Details = result
analysis.Details = parsedText
if output != "json" {
_ = bar.Add(1)
bar.Add(1)
}
a.Results[index] = analysis
}
return nil
}
func (a *Analysis) getAIResultForSanitizedFailures(texts []string, promptTmpl string) (string, error) {
inputKey := strings.Join(texts, " ")
// Check for cached data.
// TODO(bwplotka): This might depend on model too (or even other client configuration pieces), fix it in later PRs.
cacheKey := util.GetCacheKey(a.AIClient.GetName(), a.Language, inputKey)
if !a.Cache.IsCacheDisabled() && a.Cache.Exists(cacheKey) {
response, err := a.Cache.Load(cacheKey)
if err != nil {
return "", err
}
if response != "" {
output, err := base64.StdEncoding.DecodeString(response)
if err == nil {
return string(output), nil
}
color.Red("error decoding cached data; ignoring cache item: %v", err)
}
}
// Process template.
prompt := fmt.Sprintf(strings.TrimSpace(promptTmpl), a.Language, inputKey)
response, err := a.AIClient.GetCompletion(a.Context, prompt)
if err != nil {
return "", err
}
if err = a.Cache.Store(cacheKey, base64.StdEncoding.EncodeToString([]byte(response))); err != nil {
color.Red("error storing value to cache; value won't be cached: %v", err)
}
return response, nil
}
func (a *Analysis) Close() {
if a.AIClient == nil {
return
}
a.AIClient.Close()
}

View File

@@ -14,127 +14,14 @@ limitations under the License.
package analysis
import (
"context"
"encoding/json"
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/magiconair/properties/assert"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
v1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
"strings"
"testing"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/stretchr/testify/require"
)
// sub-function
func analysis_RunAnalysisFilterTester(t *testing.T, filterFlag string) []common.Result {
clientset := fake.NewSimpleClientset(
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "example",
Namespace: "default",
},
Status: v1.PodStatus{
Phase: v1.PodPending,
Conditions: []v1.PodCondition{
{
Type: v1.PodScheduled,
Reason: "Unschedulable",
Message: "0/1 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate.",
},
},
},
},
&v1.Endpoints{
ObjectMeta: metav1.ObjectMeta{
Name: "example",
Namespace: "default",
Annotations: map[string]string{},
},
},
&v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "example",
Namespace: "default",
Annotations: map[string]string{},
},
Spec: v1.ServiceSpec{
Selector: map[string]string{
"app": "example",
},
},
},
&networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "example",
Namespace: "default",
Annotations: map[string]string{},
},
},
)
analysis := Analysis{
Context: context.Background(),
Results: []common.Result{},
Namespace: "default",
MaxConcurrency: 1,
Client: &kubernetes.Client{
Client: clientset,
},
}
if len(filterFlag) > 0 {
// `--filter` is explicitly given
analysis.Filters = strings.Split(filterFlag, ",")
}
analysis.RunAnalysis()
return analysis.Results
}
// Test: Filter logic with running different Analyzers
func TestAnalysis_RunAnalysisWithFilter(t *testing.T) {
var results []common.Result
var filterFlag string
//1. Neither --filter flag Nor active filter is specified, only the "core analyzers"
results = analysis_RunAnalysisFilterTester(t, "")
assert.Equal(t, len(results), 3) // all built-in resource will be analyzed
//2. When the --filter flag is specified
filterFlag = "Pod" // --filter=Pod
results = analysis_RunAnalysisFilterTester(t, filterFlag)
assert.Equal(t, len(results), 1)
assert.Equal(t, results[0].Kind, filterFlag)
filterFlag = "Ingress,Pod" // --filter=Ingress,Pod
results = analysis_RunAnalysisFilterTester(t, filterFlag)
assert.Equal(t, len(results), 2)
}
// Test: Filter logic with Active Filter
func TestAnalysis_RunAnalysisActiveFilter(t *testing.T) {
//When the --filter flag is not specified but has actived filter in config
var results []common.Result
viper.SetDefault("active_filters", "Ingress")
results = analysis_RunAnalysisFilterTester(t, "")
assert.Equal(t, len(results), 1)
viper.SetDefault("active_filters", []string{"Ingress", "Service"})
results = analysis_RunAnalysisFilterTester(t, "")
assert.Equal(t, len(results), 2)
viper.SetDefault("active_filters", []string{"Ingress", "Service", "Pod"})
results = analysis_RunAnalysisFilterTester(t, "")
assert.Equal(t, len(results), 3)
}
func TestAnalysis_NoProblemJsonOutput(t *testing.T) {
analysis := Analysis{
@@ -163,7 +50,6 @@ func TestAnalysis_NoProblemJsonOutput(t *testing.T) {
fmt.Println(expected)
require.Equal(t, got, expected)
}
func TestAnalysis_ProblemJsonOutput(t *testing.T) {

View File

@@ -42,10 +42,8 @@ func (a *Analysis) jsonOutput() ([]byte, error) {
}
result := JsonOutput{
Provider: a.AnalysisAIProvider,
Problems: problems,
Results: a.Results,
Errors: a.Errors,
Status: status,
}
output, err := json.MarshalIndent(result, "", " ")
@@ -57,21 +55,6 @@ func (a *Analysis) jsonOutput() ([]byte, error) {
func (a *Analysis) textOutput() ([]byte, error) {
var output strings.Builder
// Print the AI provider used for this analysis (if explain was enabled).
if a.Explain {
output.WriteString(fmt.Sprintf("AI Provider: %s\n", color.YellowString(a.AnalysisAIProvider)))
} else {
output.WriteString(fmt.Sprintf("AI Provider: %s\n", color.YellowString("AI not used; --explain not set")))
}
if len(a.Errors) != 0 {
output.WriteString("\n")
output.WriteString(color.YellowString("Warnings : \n"))
for _, aerror := range a.Errors {
output.WriteString(fmt.Sprintf("- %s\n", color.YellowString(aerror)))
}
}
output.WriteString("\n")
if len(a.Results) == 0 {
output.WriteString(color.GreenString("No problems detected\n"))
@@ -82,9 +65,6 @@ func (a *Analysis) textOutput() ([]byte, error) {
color.YellowString(result.Name), color.CyanString(result.ParentObject)))
for _, err := range result.Error {
output.WriteString(fmt.Sprintf("- %s %s\n", color.RedString("Error:"), color.RedString(err.Text)))
if err.KubernetesDoc != "" {
output.WriteString(fmt.Sprintf(" %s %s\n", color.RedString("Kubernetes Doc:"), color.RedString(err.KubernetesDoc)))
}
}
output.WriteString(color.GreenString(result.Details + "\n"))
}

View File

@@ -32,27 +32,21 @@ var (
)
var coreAnalyzerMap = map[string]common.IAnalyzer{
"Pod": PodAnalyzer{},
"Deployment": DeploymentAnalyzer{},
"ReplicaSet": ReplicaSetAnalyzer{},
"PersistentVolumeClaim": PvcAnalyzer{},
"Service": ServiceAnalyzer{},
"Ingress": IngressAnalyzer{},
"StatefulSet": StatefulSetAnalyzer{},
"CronJob": CronJobAnalyzer{},
"Node": NodeAnalyzer{},
"ValidatingWebhookConfiguration": ValidatingWebhookAnalyzer{},
"MutatingWebhookConfiguration": MutatingWebhookAnalyzer{},
"Pod": PodAnalyzer{},
"Deployment": DeploymentAnalyzer{},
"ReplicaSet": ReplicaSetAnalyzer{},
"PersistentVolumeClaim": PvcAnalyzer{},
"Service": ServiceAnalyzer{},
"Ingress": IngressAnalyzer{},
"StatefulSet": StatefulSetAnalyzer{},
"CronJob": CronJobAnalyzer{},
"Node": NodeAnalyzer{},
}
var additionalAnalyzerMap = map[string]common.IAnalyzer{
"HorizontalPodAutoScaler": HpaAnalyzer{},
"PodDisruptionBudget": PdbAnalyzer{},
"NetworkPolicy": NetworkPolicyAnalyzer{},
"Log": LogAnalyzer{},
"GatewayClass": GatewayClassAnalyzer{},
"Gateway": GatewayAnalyzer{},
"HTTPRoute": HTTPRouteAnalyzer{},
}
func ListFilters() ([]string, []string, []string) {
@@ -77,27 +71,25 @@ func ListFilters() ([]string, []string, []string) {
fmt.Println(color.RedString(err.Error()))
os.Exit(1)
}
integrationAnalyzers = append(integrationAnalyzers, in.GetAnalyzerName()...)
integrationAnalyzers = append(integrationAnalyzers, in.GetAnalyzerName())
}
}
return coreKeys, additionalKeys, integrationAnalyzers
}
func GetAnalyzerMap() (map[string]common.IAnalyzer, map[string]common.IAnalyzer) {
func GetAnalyzerMap() map[string]common.IAnalyzer {
coreAnalyzer := make(map[string]common.IAnalyzer)
mergedAnalyzerMap := make(map[string]common.IAnalyzer)
mergedMap := make(map[string]common.IAnalyzer)
// add core analyzer
for key, value := range coreAnalyzerMap {
coreAnalyzer[key] = value
mergedAnalyzerMap[key] = value
mergedMap[key] = value
}
// add additional analyzer
for key, value := range additionalAnalyzerMap {
mergedAnalyzerMap[key] = value
mergedMap[key] = value
}
integrationProvider := integration.NewIntegration()
@@ -114,9 +106,9 @@ func GetAnalyzerMap() (map[string]common.IAnalyzer, map[string]common.IAnalyzer)
fmt.Println(color.RedString(err.Error()))
os.Exit(1)
}
in.AddAnalyzer(&mergedAnalyzerMap)
in.AddAnalyzer(&mergedMap)
}
}
return coreAnalyzer, mergedAnalyzerMap
return mergedMap
}

View File

@@ -18,11 +18,9 @@ import (
"time"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
cron "github.com/robfig/cron/v3"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)
type CronJobAnalyzer struct{}
@@ -30,14 +28,6 @@ type CronJobAnalyzer struct{}
func (analyzer CronJobAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
kind := "CronJob"
apiDoc := kubernetes.K8sApiReference{
Kind: kind,
ApiVersion: schema.GroupVersion{
Group: "batch",
Version: "v1",
},
OpenapiSchema: a.OpenapiSchema,
}
AnalyzerErrorsMetric.DeletePartialMatch(map[string]string{
"analyzer_name": kind,
@@ -53,11 +43,8 @@ func (analyzer CronJobAnalyzer) Analyze(a common.Analyzer) ([]common.Result, err
for _, cronJob := range cronJobList.Items {
var failures []common.Failure
if cronJob.Spec.Suspend != nil && *cronJob.Spec.Suspend {
doc := apiDoc.GetApiDocV2("spec.suspend")
failures = append(failures, common.Failure{
Text: fmt.Sprintf("CronJob %s is suspended", cronJob.Name),
KubernetesDoc: doc,
Text: fmt.Sprintf("CronJob %s is suspended", cronJob.Name),
Sensitive: []common.Sensitive{
{
Unmasked: cronJob.Namespace,
@@ -72,11 +59,8 @@ func (analyzer CronJobAnalyzer) Analyze(a common.Analyzer) ([]common.Result, err
} else {
// check the schedule format
if _, err := CheckCronScheduleIsValid(cronJob.Spec.Schedule); err != nil {
doc := apiDoc.GetApiDocV2("spec.schedule")
failures = append(failures, common.Failure{
Text: fmt.Sprintf("CronJob %s has an invalid schedule: %s", cronJob.Name, err.Error()),
KubernetesDoc: doc,
Text: fmt.Sprintf("CronJob %s has an invalid schedule: %s", cronJob.Name, err.Error()),
Sensitive: []common.Sensitive{
{
Unmasked: cronJob.Namespace,
@@ -94,11 +78,9 @@ func (analyzer CronJobAnalyzer) Analyze(a common.Analyzer) ([]common.Result, err
if cronJob.Spec.StartingDeadlineSeconds != nil {
deadline := time.Duration(*cronJob.Spec.StartingDeadlineSeconds) * time.Second
if deadline < 0 {
doc := apiDoc.GetApiDocV2("spec.startingDeadlineSeconds")
failures = append(failures, common.Failure{
Text: fmt.Sprintf("CronJob %s has a negative starting deadline", cronJob.Name),
KubernetesDoc: doc,
Text: fmt.Sprintf("CronJob %s has a negative starting deadline", cronJob.Name),
Sensitive: []common.Sensitive{
{
Unmasked: cronJob.Namespace,

View File

@@ -18,10 +18,8 @@ import (
"fmt"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
)
@@ -33,14 +31,6 @@ type DeploymentAnalyzer struct {
func (d DeploymentAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
kind := "Deployment"
apiDoc := kubernetes.K8sApiReference{
Kind: kind,
ApiVersion: schema.GroupVersion{
Group: "apps",
Version: "v1",
},
OpenapiSchema: a.OpenapiSchema,
}
AnalyzerErrorsMetric.DeletePartialMatch(map[string]string{
"analyzer_name": kind,
@@ -55,11 +45,8 @@ func (d DeploymentAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error)
for _, deployment := range deployments.Items {
var failures []common.Failure
if *deployment.Spec.Replicas != deployment.Status.Replicas {
doc := apiDoc.GetApiDocV2("spec.replicas")
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Deployment %s/%s has %d replicas but %d are available", deployment.Namespace, deployment.Name, *deployment.Spec.Replicas, deployment.Status.Replicas),
KubernetesDoc: doc,
Text: fmt.Sprintf("Deployment %s/%s has %d replicas but %d are available", deployment.Namespace, deployment.Name, *deployment.Spec.Replicas, deployment.Status.Replicas),
Sensitive: []common.Sensitive{
{
Unmasked: deployment.Namespace,

View File

@@ -1,108 +0,0 @@
/*
Copyright 2023 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package analyzer
import (
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime/pkg/client"
gtwapi "sigs.k8s.io/gateway-api/apis/v1"
)
type GatewayAnalyzer struct{}
// Gateway analyser will analyse all different Kinds and search for missing object dependencies
func (GatewayAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
kind := "Gateway"
AnalyzerErrorsMetric.DeletePartialMatch(map[string]string{
"analyzer_name": kind,
})
gtwList := &gtwapi.GatewayList{}
gc := &gtwapi.GatewayClass{}
client := a.Client.CtrlClient
gtwapi.AddToScheme(client.Scheme())
if err := client.List(a.Context, gtwList, &ctrl.ListOptions{}); err != nil {
return nil, err
}
var preAnalysis = map[string]common.PreAnalysis{}
// Find all unhealthy gateway Classes
for _, gtw := range gtwList.Items {
var failures []common.Failure
gtwName := gtw.GetName()
gtwNamespace := gtw.GetNamespace()
// Check if gatewayclass exists
err := client.Get(a.Context, ctrl.ObjectKey{Namespace: gtwNamespace, Name: string(gtw.Spec.GatewayClassName)}, gc, &ctrl.GetOptions{})
if errors.IsNotFound(err) {
failures = append(failures, common.Failure{
Text: fmt.Sprintf(
"Gateway uses the GatewayClass %s which does not exist.",
gtw.Spec.GatewayClassName,
),
Sensitive: []common.Sensitive{
{
Unmasked: string(gtw.Spec.GatewayClassName),
Masked: util.MaskString(string(gtw.Spec.GatewayClassName)),
},
},
})
}
// Check only the current conditions
// TODO: maybe check other statuses Listeners, addresses?
if gtw.Status.Conditions[0].Status != metav1.ConditionTrue {
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Gateway '%s/%s' is not accepted. Message: '%s'.",
gtwNamespace,
gtwName,
gtw.Status.Conditions[0].Message,
),
Sensitive: []common.Sensitive{
{
Unmasked: gtwNamespace,
Masked: util.MaskString(gtwNamespace),
},
{
Unmasked: gtwName,
Masked: util.MaskString(gtwName),
},
},
})
}
if len(failures) > 0 {
preAnalysis[fmt.Sprintf("%s/%s", gtwNamespace, gtwName)] = common.PreAnalysis{
Gateway: gtw,
FailureDetails: failures,
}
AnalyzerErrorsMetric.WithLabelValues(kind, gtwName, gtwNamespace).Set(float64(len(failures)))
}
}
for key, value := range preAnalysis {
var currentAnalysis = common.Result{
Kind: kind,
Name: key,
Error: value.FailureDetails,
}
a.Results = append(a.Results, currentAnalysis)
}
return a.Results, nil
}

View File

@@ -1,161 +0,0 @@
package analyzer
import (
"context"
"testing"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/magiconair/properties/assert"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/scheme"
fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
gtwapi "sigs.k8s.io/gateway-api/apis/v1"
)
func BuildGatewayClass(name string) gtwapi.GatewayClass {
GatewayClass := gtwapi.GatewayClass{}
GatewayClass.Name = name
// Namespace is not needed outside of this test, GatewayClass is cluster-scoped
GatewayClass.Namespace = "default"
GatewayClass.Spec.ControllerName = "gateway.fooproxy.io/gatewayclass-controller"
return GatewayClass
}
func BuildGateway(className gtwapi.ObjectName, status metav1.ConditionStatus) gtwapi.Gateway {
Gateway := gtwapi.Gateway{}
Gateway.Name = "foobar"
Gateway.Namespace = "default"
Gateway.Spec.GatewayClassName = className
Gateway.Spec.Listeners = []gtwapi.Listener{
{
Name: "proxy",
Port: 80,
Protocol: gtwapi.HTTPProtocolType,
},
}
Condition := metav1.Condition{
Type: "Accepted",
Status: status,
Message: "An expected message",
Reason: "Test",
}
Gateway.Status.Conditions = []metav1.Condition{Condition}
return Gateway
}
func TestGatewayAnalyzer(t *testing.T) {
ClassName := gtwapi.ObjectName("exists")
AcceptedStatus := metav1.ConditionTrue
GatewayClass := BuildGatewayClass(string(ClassName))
Gateway := BuildGateway(ClassName, AcceptedStatus)
// Create a Gateway Analyzer instance with the fake client
scheme := scheme.Scheme
gtwapi.Install(scheme)
apiextensionsv1.AddToScheme(scheme)
objects := []runtime.Object{
&Gateway,
&GatewayClass,
}
fakeClient := fakeclient.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(objects...).Build()
analyzerInstance := GatewayAnalyzer{}
config := common.Analyzer{
Client: &kubernetes.Client{
CtrlClient: fakeClient,
},
Context: context.Background(),
Namespace: "default",
}
analysisResults, err := analyzerInstance.Analyze(config)
if err != nil {
t.Error(err)
}
assert.Equal(t, len(analysisResults), 0)
}
func TestMissingClassGatewayAnalyzer(t *testing.T) {
ClassName := gtwapi.ObjectName("non-existed")
AcceptedStatus := metav1.ConditionTrue
Gateway := BuildGateway(ClassName, AcceptedStatus)
// Create a Gateway Analyzer instance with the fake client
scheme := scheme.Scheme
gtwapi.Install(scheme)
apiextensionsv1.AddToScheme(scheme)
objects := []runtime.Object{
&Gateway,
}
fakeClient := fakeclient.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(objects...).Build()
analyzerInstance := GatewayAnalyzer{}
config := common.Analyzer{
Client: &kubernetes.Client{
CtrlClient: fakeClient,
},
Context: context.Background(),
Namespace: "default",
}
analysisResults, err := analyzerInstance.Analyze(config)
if err != nil {
t.Error(err)
}
assert.Equal(t, len(analysisResults), 1)
}
func TestStatusGatewayAnalyzer(t *testing.T) {
ClassName := gtwapi.ObjectName("exists")
AcceptedStatus := metav1.ConditionUnknown
GatewayClass := BuildGatewayClass(string(ClassName))
Gateway := BuildGateway(ClassName, AcceptedStatus)
// Create a Gateway Analyzer instance with the fake client
scheme := scheme.Scheme
gtwapi.Install(scheme)
apiextensionsv1.AddToScheme(scheme)
objects := []runtime.Object{
&Gateway,
&GatewayClass,
}
fakeClient := fakeclient.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(objects...).Build()
analyzerInstance := GatewayAnalyzer{}
config := common.Analyzer{
Client: &kubernetes.Client{
CtrlClient: fakeClient,
},
Context: context.Background(),
Namespace: "default",
}
analysisResults, err := analyzerInstance.Analyze(config)
if err != nil {
t.Error(err)
}
var errorFound bool
want := "Gateway 'default/foobar' is not accepted. Message: 'An expected message'."
for _, analysis := range analysisResults {
for _, got := range analysis.Error {
if want == got.Text {
errorFound = true
}
}
if errorFound {
break
}
}
if !errorFound {
t.Errorf("Expected message, <%v> , not found in Gateway's analysis results", want)
}
}

View File

@@ -1,84 +0,0 @@
/*
Copyright 2023 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package analyzer
import (
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime/pkg/client"
gtwapi "sigs.k8s.io/gateway-api/apis/v1"
)
type GatewayClassAnalyzer struct{}
// Gateway analyser will analyse all different Kinds and search for missing object dependencies
func (GatewayClassAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
kind := "GatewayClass"
AnalyzerErrorsMetric.DeletePartialMatch(map[string]string{
"analyzer_name": kind,
})
gcList := &gtwapi.GatewayClassList{}
client := a.Client.CtrlClient
gtwapi.AddToScheme(client.Scheme())
if err := client.List(a.Context, gcList, &ctrl.ListOptions{}); err != nil {
return nil, err
}
var preAnalysis = map[string]common.PreAnalysis{}
// Find all unhealthy gateway Classes
for _, gc := range gcList.Items {
var failures []common.Failure
gcName := gc.GetName()
// Check only the current condition
if gc.Status.Conditions[0].Status != metav1.ConditionTrue {
failures = append(failures, common.Failure{
Text: fmt.Sprintf(
"GatewayClass '%s' with a controller name '%s' is not accepted. Message: '%s'.",
gcName,
gc.Spec.ControllerName,
gc.Status.Conditions[0].Message,
),
Sensitive: []common.Sensitive{
{
Unmasked: gcName,
Masked: util.MaskString(gcName),
},
},
})
}
if len(failures) > 0 {
preAnalysis[gcName] = common.PreAnalysis{
GatewayClass: gc,
FailureDetails: failures,
}
AnalyzerErrorsMetric.WithLabelValues(kind, gcName, "").Set(float64(len(failures)))
}
}
for key, value := range preAnalysis {
var currentAnalysis = common.Result{
Kind: kind,
Name: key,
Error: value.FailureDetails,
}
a.Results = append(a.Results, currentAnalysis)
}
return a.Results, nil
}

View File

@@ -1,51 +0,0 @@
package analyzer
import (
"context"
"testing"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/stretchr/testify/assert"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/scheme"
fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
gtwapi "sigs.k8s.io/gateway-api/apis/v1"
)
// Testing with the fake dynamic client if GatewayClasses have an accepted status
func TestGatewayClassAnalyzer(t *testing.T) {
GatewayClass := &gtwapi.GatewayClass{}
GatewayClass.Name = "foobar"
GatewayClass.Spec.ControllerName = "gateway.fooproxy.io/gatewayclass-controller"
// Initialize Conditions slice before setting properties
BadCondition := metav1.Condition{
Type: "Accepted",
Status: "Uknown",
Message: "Waiting for controller",
Reason: "Pending",
}
GatewayClass.Status.Conditions = []metav1.Condition{BadCondition}
// Create a GatewayClassAnalyzer instance with the fake client
scheme := scheme.Scheme
gtwapi.Install(scheme)
apiextensionsv1.AddToScheme(scheme)
fakeClient := fakeclient.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(GatewayClass).Build()
analyzerInstance := GatewayClassAnalyzer{}
config := common.Analyzer{
Client: &kubernetes.Client{
CtrlClient: fakeClient,
},
Context: context.Background(),
Namespace: "default",
}
analysisResults, err := analyzerInstance.Analyze(config)
if err != nil {
t.Error(err)
}
assert.Equal(t, len(analysisResults), 1)
}

View File

@@ -17,12 +17,10 @@ import (
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)
type HpaAnalyzer struct{}
@@ -30,14 +28,6 @@ type HpaAnalyzer struct{}
func (HpaAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
kind := "HorizontalPodAutoscaler"
apiDoc := kubernetes.K8sApiReference{
Kind: kind,
ApiVersion: schema.GroupVersion{
Group: "autoscaling",
Version: "v1",
},
OpenapiSchema: a.OpenapiSchema,
}
AnalyzerErrorsMetric.DeletePartialMatch(map[string]string{
"analyzer_name": kind,
@@ -86,11 +76,8 @@ func (HpaAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
}
if podInfo == nil {
doc := apiDoc.GetApiDocV2("spec.scaleTargetRef")
failures = append(failures, common.Failure{
Text: fmt.Sprintf("HorizontalPodAutoscaler uses %s/%s as ScaleTargetRef which does not exist.", scaleTargetRef.Kind, scaleTargetRef.Name),
KubernetesDoc: doc,
Text: fmt.Sprintf("HorizontalPodAutoscaler uses %s/%s as ScaleTargetRef which does not exist.", scaleTargetRef.Kind, scaleTargetRef.Name),
Sensitive: []common.Sensitive{
{
Unmasked: scaleTargetRef.Name,
@@ -107,11 +94,8 @@ func (HpaAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
}
if containers <= 0 {
doc := apiDoc.GetApiDocV2("spec.scaleTargetRef.kind")
failures = append(failures, common.Failure{
Text: fmt.Sprintf("%s %s/%s does not have resource configured.", scaleTargetRef.Kind, a.Namespace, scaleTargetRef.Name),
KubernetesDoc: doc,
Text: fmt.Sprintf("%s %s/%s does not have resource configured.", scaleTargetRef.Kind, a.Namespace, scaleTargetRef.Name),
Sensitive: []common.Sensitive{
{
Unmasked: scaleTargetRef.Name,

View File

@@ -205,15 +205,15 @@ func TestHPAAnalyzerWithExistingScaleTargetRefAsDeployment(t *testing.T) {
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "example",
Name: "example",
Image: "nginx",
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
"cpu": resource.MustParse("100m"),
"cpu": resource.MustParse("100m"),
"memory": resource.MustParse("128Mi"),
},
Limits: corev1.ResourceList{
"cpu": resource.MustParse("200m"),
"cpu": resource.MustParse("200m"),
"memory": resource.MustParse("256Mi"),
},
},
@@ -269,15 +269,15 @@ func TestHPAAnalyzerWithExistingScaleTargetRefAsReplicationController(t *testing
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "example",
Name: "example",
Image: "nginx",
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
"cpu": resource.MustParse("100m"),
"cpu": resource.MustParse("100m"),
"memory": resource.MustParse("128Mi"),
},
Limits: corev1.ResourceList{
"cpu": resource.MustParse("200m"),
"cpu": resource.MustParse("200m"),
"memory": resource.MustParse("256Mi"),
},
},
@@ -333,15 +333,15 @@ func TestHPAAnalyzerWithExistingScaleTargetRefAsReplicaSet(t *testing.T) {
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "example",
Name: "example",
Image: "nginx",
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
"cpu": resource.MustParse("100m"),
"cpu": resource.MustParse("100m"),
"memory": resource.MustParse("128Mi"),
},
Limits: corev1.ResourceList{
"cpu": resource.MustParse("200m"),
"cpu": resource.MustParse("200m"),
"memory": resource.MustParse("256Mi"),
},
},
@@ -397,15 +397,15 @@ func TestHPAAnalyzerWithExistingScaleTargetRefAsStatefulSet(t *testing.T) {
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "example",
Name: "example",
Image: "nginx",
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
"cpu": resource.MustParse("100m"),
"cpu": resource.MustParse("100m"),
"memory": resource.MustParse("128Mi"),
},
Limits: corev1.ResourceList{
"cpu": resource.MustParse("200m"),
"cpu": resource.MustParse("200m"),
"memory": resource.MustParse("256Mi"),
},
},
@@ -461,7 +461,7 @@ func TestHPAAnalyzerWithExistingScaleTargetRefWithoutSpecifyingResources(t *test
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "example",
Name: "example",
Image: "nginx",
},
},
@@ -487,7 +487,7 @@ func TestHPAAnalyzerWithExistingScaleTargetRefWithoutSpecifyingResources(t *test
var errorFound bool
for _, analysis := range analysisResults {
for _, err := range analysis.Error {
if strings.Contains(err.Text, "does not have resource configured.") {
if strings.Contains(err.Text, "does not have resource configured."){
errorFound = true
break
}

View File

@@ -1,228 +0,0 @@
/*
Copyright 2023 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package analyzer
import (
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
ctrl "sigs.k8s.io/controller-runtime/pkg/client"
gtwapi "sigs.k8s.io/gateway-api/apis/v1"
)
type HTTPRouteAnalyzer struct{}
// Gateway analyser will analyse all different Kinds and search for missing object dependencies
func (HTTPRouteAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
kind := "HTTPRoute"
AnalyzerErrorsMetric.DeletePartialMatch(map[string]string{
"analyzer_name": kind,
})
routeList := &gtwapi.HTTPRouteList{}
gtw := &gtwapi.Gateway{}
service := &corev1.Service{}
client := a.Client.CtrlClient
gtwapi.AddToScheme(client.Scheme())
if err := client.List(a.Context, routeList, &ctrl.ListOptions{}); err != nil {
return nil, err
}
var preAnalysis = map[string]common.PreAnalysis{}
// Find all unhealthy gateway Classes
for _, route := range routeList.Items {
var failures []common.Failure
// Check if Gateways exists in the same or designated namespace
// TODO: when meshes and ClusterIp options are adopted we can add more checks
// e.g Service Port matching
for _, gtwref := range route.Spec.ParentRefs {
namespace := route.Namespace
if gtwref.Namespace != nil {
namespace = string(*gtwref.Namespace)
}
err := client.Get(a.Context, ctrl.ObjectKey{Namespace: namespace, Name: string(gtwref.Name)}, gtw, &ctrl.GetOptions{})
if errors.IsNotFound(err) {
failures = append(failures, common.Failure{
Text: fmt.Sprintf(
"HTTPRoute uses the Gateway '%s/%s' which does not exist in the same namespace.",
namespace,
gtwref.Name,
),
Sensitive: []common.Sensitive{
{
Unmasked: gtw.Namespace,
Masked: util.MaskString(gtw.Namespace),
},
{
Unmasked: gtw.Name,
Masked: util.MaskString(gtw.Name),
},
},
})
} else {
// Check if the aforementioned Gateway allows the HTTPRoutes from the route's namespace
for _, listener := range gtw.Spec.Listeners {
if listener.AllowedRoutes.Namespaces != nil {
switch allow := listener.AllowedRoutes.Namespaces.From; {
case *allow == gtwapi.NamespacesFromSame:
// check if Gateway is in the same namespace
if route.Namespace != gtw.Namespace {
failures = append(failures, common.Failure{
Text: fmt.Sprintf("HTTPRoute '%s/%s' is deployed in a different namespace from Gateway '%s/%s' which only allows HTTPRoutes from its namespace.",
route.Namespace,
route.Name,
gtw.Namespace,
gtw.Name,
),
Sensitive: []common.Sensitive{
{
Unmasked: route.Namespace,
Masked: util.MaskString(route.Namespace),
},
{
Unmasked: route.Name,
Masked: util.MaskString(route.Name),
},
{
Unmasked: gtw.Namespace,
Masked: util.MaskString(gtw.Namespace),
},
{
Unmasked: gtw.Name,
Masked: util.MaskString(gtw.Name),
},
},
})
}
case *allow == gtwapi.NamespacesFromSelector:
// check if our route include the same selector Label
if !util.LabelsIncludeAny(listener.AllowedRoutes.Namespaces.Selector.MatchLabels, route.Labels) {
failures = append(failures, common.Failure{
Text: fmt.Sprintf(
"HTTPRoute '%s/%s' can't be attached on Gateway '%s/%s', selector labels do not match HTTProute's labels.",
route.Namespace,
route.Name,
gtw.Namespace,
gtw.Name,
),
Sensitive: []common.Sensitive{
{
Unmasked: route.Namespace,
Masked: util.MaskString(route.Namespace),
},
{
Unmasked: route.Name,
Masked: util.MaskString(route.Name),
},
{
Unmasked: gtw.Namespace,
Masked: util.MaskString(gtw.Namespace),
},
{
Unmasked: gtw.Name,
Masked: util.MaskString(gtw.Name),
},
},
})
}
}
}
}
}
}
// Check if the Backends are valid services and ports are matching with services Ports
for _, rule := range route.Spec.Rules {
for _, backend := range rule.BackendRefs {
err := client.Get(a.Context, ctrl.ObjectKey{Namespace: route.Namespace, Name: string(backend.Name)}, service, &ctrl.GetOptions{})
if errors.IsNotFound(err) {
failures = append(failures, common.Failure{
Text: fmt.Sprintf(
"HTTPRoute uses the Service '%s/%s' which does not exist.",
route.Namespace,
backend.Name,
),
Sensitive: []common.Sensitive{
{
Unmasked: service.Namespace,
Masked: util.MaskString(service.Namespace),
},
{
Unmasked: service.Name,
Masked: util.MaskString(service.Name),
},
},
})
} else {
portMatch := false
for _, svcPort := range service.Spec.Ports {
if int32(*backend.Port) == svcPort.Port {
portMatch = true
}
}
if !portMatch {
failures = append(failures, common.Failure{
Text: fmt.Sprintf(
"HTTPRoute's backend service '%s' is using port '%d' but the corresponding K8s service '%s/%s' isn't configured with the same port.",
backend.Name,
int32(*backend.Port),
service.Namespace,
service.Name,
),
Sensitive: []common.Sensitive{
{
Unmasked: string(backend.Name),
Masked: util.MaskString(string(backend.Name)),
},
{
Unmasked: service.Name,
Masked: util.MaskString(service.Name),
},
{
Unmasked: service.Namespace,
Masked: service.Namespace,
},
},
})
}
}
}
}
if len(failures) > 0 {
preAnalysis[fmt.Sprintf("%s/%s", route.Namespace, route.Name)] = common.PreAnalysis{
HTTPRoute: route,
FailureDetails: failures,
}
AnalyzerErrorsMetric.WithLabelValues(kind, route.Name, route.Namespace).Set(float64(len(failures)))
}
}
for key, value := range preAnalysis {
var currentAnalysis = common.Result{
Kind: kind,
Name: key,
Error: value.FailureDetails,
}
a.Results = append(a.Results, currentAnalysis)
}
return a.Results, nil
}

View File

@@ -1,374 +0,0 @@
package analyzer
import (
"context"
"testing"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
corev1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/kubernetes/scheme"
fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
gtwapi "sigs.k8s.io/gateway-api/apis/v1"
)
func BuildRouteGateway(namespace, name, fromNamespaceref string) gtwapi.Gateway {
routeNamespace := &gtwapi.RouteNamespaces{}
switch fromNamespaceref {
case "Same":
fromSame := gtwapi.NamespacesFromSame
routeNamespace.From = &fromSame
case "Selector":
fromSelector := gtwapi.NamespacesFromSelector
routeNamespace.From = &fromSelector
routeNamespace.Selector = &metav1.LabelSelector{}
routeNamespace.Selector.MatchLabels = map[string]string{"foo": "bar"}
default:
fromAll := gtwapi.NamespacesFromAll
routeNamespace.From = &fromAll
}
Gateway := gtwapi.Gateway{}
Gateway.Name = name
Gateway.Namespace = namespace
Gateway.Spec.GatewayClassName = "fooclassName"
Gateway.Spec.Listeners = []gtwapi.Listener{
{
Name: "proxy",
Port: 80,
Protocol: gtwapi.HTTPProtocolType,
AllowedRoutes: &gtwapi.AllowedRoutes{
Namespaces: routeNamespace,
},
},
}
Condition := metav1.Condition{
Type: "Accepted",
Status: "True",
Message: "An expected message",
Reason: "Test",
}
Gateway.Status.Conditions = []metav1.Condition{Condition}
return Gateway
}
func BuildHTTPRoute(backendName, gtwName gtwapi.ObjectName, gtwNamespace gtwapi.Namespace, svcPort *gtwapi.PortNumber, namespace string) gtwapi.HTTPRoute {
HTTPRoute := gtwapi.HTTPRoute{}
HTTPRoute.Name = "foohttproute"
HTTPRoute.Namespace = namespace
HTTPRoute.Spec.ParentRefs = []gtwapi.ParentReference{
{
Name: gtwName,
Namespace: &gtwNamespace,
},
}
HTTPRoute.Spec.Rules = []gtwapi.HTTPRouteRule{
{
BackendRefs: []gtwapi.HTTPBackendRef{
{
BackendRef: gtwapi.BackendRef{
BackendObjectReference: gtwapi.BackendObjectReference{
Name: backendName,
Port: svcPort,
},
},
},
},
},
}
return HTTPRoute
}
/*
Testing different cases
1. Gateway doesn't exist or at least doesn't exist in the same namespace
2. Gateway exists in different namespace, is configured in httproute's spec
and Gateway's configuration is allowing only from its same namespace
3. Gateway exists in the same namespace but has selectors different from route's labels
4. BackendRef is pointing to a non existent Service
5. BackendRef's port and Service Port are different
*/
func TestGWMissiningHTTRouteAnalyzer(t *testing.T) {
backendName := gtwapi.ObjectName("foobackend")
gtwName := gtwapi.ObjectName("non-existent")
gtwNamespace := gtwapi.Namespace("non-existent")
svcPort := gtwapi.PortNumber(1027)
httpRouteNamespace := "default"
HTTPRoute := BuildHTTPRoute(backendName, gtwName, gtwNamespace, &svcPort, httpRouteNamespace)
// Create a Gateway Analyzer instance with the fake client
scheme := scheme.Scheme
gtwapi.Install(scheme)
apiextensionsv1.AddToScheme(scheme)
objects := []runtime.Object{
&HTTPRoute,
}
fakeClient := fakeclient.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(objects...).Build()
analyzerInstance := HTTPRouteAnalyzer{}
config := common.Analyzer{
Client: &kubernetes.Client{
CtrlClient: fakeClient,
},
Context: context.Background(),
Namespace: "default",
}
analysisResults, err := analyzerInstance.Analyze(config)
if err != nil {
t.Error(err)
}
var errorFound bool
want := "HTTPRoute uses the Gateway 'non-existent/non-existent' which does not exist in the same namespace."
for _, analysis := range analysisResults {
for _, got := range analysis.Error {
if want == got.Text {
errorFound = true
}
}
if errorFound {
break
}
}
if !errorFound {
t.Errorf("Expected message, <%s> , not found in HTTPRoute's analysis results", want)
}
}
func TestGWConfigSameHTTRouteAnalyzer(t *testing.T) {
backendName := gtwapi.ObjectName("foobackend")
gtwName := gtwapi.ObjectName("gatewayname")
gtwNamespace := gtwapi.Namespace("differentnamespace")
svcPort := gtwapi.PortNumber(1027)
httpRouteNamespace := "default"
HTTPRoute := BuildHTTPRoute(backendName, gtwName, gtwNamespace, &svcPort, httpRouteNamespace)
Gateway := BuildRouteGateway("differentnamespace", "gatewayname", "Same")
// Create a Gateway Analyzer instance with the fake client
scheme := scheme.Scheme
gtwapi.Install(scheme)
apiextensionsv1.AddToScheme(scheme)
objects := []runtime.Object{
&HTTPRoute,
&Gateway,
}
fakeClient := fakeclient.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(objects...).Build()
analyzerInstance := HTTPRouteAnalyzer{}
config := common.Analyzer{
Client: &kubernetes.Client{
CtrlClient: fakeClient,
},
Context: context.Background(),
Namespace: "default",
}
analysisResults, err := analyzerInstance.Analyze(config)
if err != nil {
t.Error(err)
}
var errorFound bool
want := "HTTPRoute 'default/foohttproute' is deployed in a different namespace from Gateway 'differentnamespace/gatewayname' which only allows HTTPRoutes from its namespace."
for _, analysis := range analysisResults {
for _, got := range analysis.Error {
if want == got.Text {
errorFound = true
}
}
if errorFound {
break
}
}
if !errorFound {
t.Errorf("Expected message, <%s> , not found in HTTPRoute's analysis results", want)
}
}
func TestGWConfigSelectorHTTRouteAnalyzer(t *testing.T) {
backendName := gtwapi.ObjectName("foobackend")
gtwName := gtwapi.ObjectName("gatewayname")
gtwNamespace := gtwapi.Namespace("default")
svcPort := gtwapi.PortNumber(1027)
httpRouteNamespace := "default"
HTTPRoute := BuildHTTPRoute(backendName, gtwName, gtwNamespace, &svcPort, httpRouteNamespace)
Gateway := BuildRouteGateway("default", "gatewayname", "Selector")
// Create a Gateway Analyzer instance with the fake client
scheme := scheme.Scheme
gtwapi.Install(scheme)
apiextensionsv1.AddToScheme(scheme)
objects := []runtime.Object{
&HTTPRoute,
&Gateway,
}
fakeClient := fakeclient.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(objects...).Build()
analyzerInstance := HTTPRouteAnalyzer{}
config := common.Analyzer{
Client: &kubernetes.Client{
CtrlClient: fakeClient,
},
Context: context.Background(),
Namespace: "default",
}
analysisResults, err := analyzerInstance.Analyze(config)
if err != nil {
t.Error(err)
}
var errorFound bool
want := "HTTPRoute 'default/foohttproute' can't be attached on Gateway 'default/gatewayname', selector labels do not match HTTProute's labels."
for _, analysis := range analysisResults {
for _, got := range analysis.Error {
if want == got.Text {
errorFound = true
}
}
if errorFound {
break
}
}
if !errorFound {
t.Errorf("Expected message, <%s> , not found in HTTPRoute's analysis results", want)
}
}
func TestSvcMissingHTTRouteAnalyzer(t *testing.T) {
backendName := gtwapi.ObjectName("foobackend")
gtwName := gtwapi.ObjectName("gatewayname")
gtwNamespace := gtwapi.Namespace("default")
svcPort := gtwapi.PortNumber(1027)
httpRouteNamespace := "default"
HTTPRoute := BuildHTTPRoute(backendName, gtwName, gtwNamespace, &svcPort, httpRouteNamespace)
Gateway := BuildRouteGateway("default", "gatewayname", "Same")
// Create a Gateway Analyzer instance with the fake client
scheme := scheme.Scheme
gtwapi.Install(scheme)
apiextensionsv1.AddToScheme(scheme)
objects := []runtime.Object{
&HTTPRoute,
&Gateway,
}
fakeClient := fakeclient.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(objects...).Build()
analyzerInstance := HTTPRouteAnalyzer{}
config := common.Analyzer{
Client: &kubernetes.Client{
CtrlClient: fakeClient,
},
Context: context.Background(),
Namespace: "default",
}
analysisResults, err := analyzerInstance.Analyze(config)
if err != nil {
t.Error(err)
}
var errorFound bool
want := "HTTPRoute uses the Service 'default/foobackend' which does not exist."
for _, analysis := range analysisResults {
for _, got := range analysis.Error {
if want == got.Text {
errorFound = true
}
}
if errorFound {
break
}
}
if !errorFound {
t.Errorf("Expected message, <%s> , not found in HTTPRoute's analysis results", want)
}
}
func TestSvcDifferentPortHTTRouteAnalyzer(t *testing.T) {
//Add a Service Object
Service := corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "foobackend",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Selector: map[string]string{
"app": "example-app",
},
Ports: []corev1.ServicePort{
{
Name: "http",
Protocol: "TCP",
Port: 80,
TargetPort: intstr.FromInt(8080),
},
},
Type: corev1.ServiceTypeClusterIP,
},
}
backendName := gtwapi.ObjectName("foobackend")
gtwName := gtwapi.ObjectName("gatewayname")
gtwNamespace := gtwapi.Namespace("default")
// different port
svcPort := gtwapi.PortNumber(1027)
httpRouteNamespace := "default"
HTTPRoute := BuildHTTPRoute(backendName, gtwName, gtwNamespace, &svcPort, httpRouteNamespace)
Gateway := BuildRouteGateway("default", "gatewayname", "Same")
// Create a Gateway Analyzer instance with the fake client
scheme := scheme.Scheme
gtwapi.Install(scheme)
apiextensionsv1.AddToScheme(scheme)
objects := []runtime.Object{
&HTTPRoute,
&Gateway,
&Service,
}
fakeClient := fakeclient.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(objects...).Build()
analyzerInstance := HTTPRouteAnalyzer{}
config := common.Analyzer{
Client: &kubernetes.Client{
CtrlClient: fakeClient,
},
Context: context.Background(),
Namespace: "default",
}
analysisResults, err := analyzerInstance.Analyze(config)
if err != nil {
t.Error(err)
}
var errorFound bool
want := "HTTPRoute's backend service 'foobackend' is using port '1027' but the corresponding K8s service 'default/foobackend' isn't configured with the same port."
for _, analysis := range analysisResults {
for _, got := range analysis.Error {
if want == got.Text {
errorFound = true
}
}
if errorFound {
break
}
}
if !errorFound {
t.Errorf("Expected message, <%s> , not found in HTTPRoute's analysis results", want)
}
}

View File

@@ -17,10 +17,8 @@ import (
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)
type IngressAnalyzer struct{}
@@ -28,14 +26,6 @@ type IngressAnalyzer struct{}
func (IngressAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
kind := "Ingress"
apiDoc := kubernetes.K8sApiReference{
Kind: kind,
ApiVersion: schema.GroupVersion{
Group: "networking",
Version: "v1",
},
OpenapiSchema: a.OpenapiSchema,
}
AnalyzerErrorsMetric.DeletePartialMatch(map[string]string{
"analyzer_name": kind,
@@ -56,11 +46,8 @@ func (IngressAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
if ingressClassName == nil {
ingClassValue := ing.Annotations["kubernetes.io/ingress.class"]
if ingClassValue == "" {
doc := apiDoc.GetApiDocV2("spec.ingressClassName")
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Ingress %s/%s does not specify an Ingress class.", ing.Namespace, ing.Name),
KubernetesDoc: doc,
Text: fmt.Sprintf("Ingress %s/%s does not specify an Ingress class.", ing.Namespace, ing.Name),
Sensitive: []common.Sensitive{
{
Unmasked: ing.Namespace,
@@ -81,11 +68,8 @@ func (IngressAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
if ingressClassName != nil {
_, err := a.Client.GetClient().NetworkingV1().IngressClasses().Get(a.Context, *ingressClassName, metav1.GetOptions{})
if err != nil {
doc := apiDoc.GetApiDocV2("spec.ingressClassName")
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Ingress uses the ingress class %s which does not exist.", *ingressClassName),
KubernetesDoc: doc,
Text: fmt.Sprintf("Ingress uses the ingress class %s which does not exist.", *ingressClassName),
Sensitive: []common.Sensitive{
{
Unmasked: *ingressClassName,
@@ -98,28 +82,23 @@ func (IngressAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
// loop over rules
for _, rule := range ing.Spec.Rules {
// loop over HTTP paths
if rule.HTTP != nil {
for _, path := range rule.HTTP.Paths {
_, err := a.Client.GetClient().CoreV1().Services(ing.Namespace).Get(a.Context, path.Backend.Service.Name, metav1.GetOptions{})
if err != nil {
doc := apiDoc.GetApiDocV2("spec.rules.http.paths.backend.service")
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Ingress uses the service %s/%s which does not exist.", ing.Namespace, path.Backend.Service.Name),
KubernetesDoc: doc,
Sensitive: []common.Sensitive{
{
Unmasked: ing.Namespace,
Masked: util.MaskString(ing.Namespace),
},
{
Unmasked: path.Backend.Service.Name,
Masked: util.MaskString(path.Backend.Service.Name),
},
// loop over paths
for _, path := range rule.HTTP.Paths {
_, err := a.Client.GetClient().CoreV1().Services(ing.Namespace).Get(a.Context, path.Backend.Service.Name, metav1.GetOptions{})
if err != nil {
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Ingress uses the service %s/%s which does not exist.", ing.Namespace, path.Backend.Service.Name),
Sensitive: []common.Sensitive{
{
Unmasked: ing.Namespace,
Masked: util.MaskString(ing.Namespace),
},
})
}
{
Unmasked: path.Backend.Service.Name,
Masked: util.MaskString(path.Backend.Service.Name),
},
},
})
}
}
}
@@ -127,11 +106,8 @@ func (IngressAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
for _, tls := range ing.Spec.TLS {
_, err := a.Client.GetClient().CoreV1().Secrets(ing.Namespace).Get(a.Context, tls.SecretName, metav1.GetOptions{})
if err != nil {
doc := apiDoc.GetApiDocV2("spec.tls.secretName")
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Ingress uses the secret %s/%s as a TLS certificate which does not exist.", ing.Namespace, tls.SecretName),
KubernetesDoc: doc,
Text: fmt.Sprintf("Ingress uses the secret %s/%s as a TLS certificate which does not exist.", ing.Namespace, tls.SecretName),
Sensitive: []common.Sensitive{
{
Unmasked: ing.Namespace,

View File

@@ -1,116 +0,0 @@
/*
Copyright 2023 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package analyzer
import (
"fmt"
"regexp"
"strings"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
var (
errorPattern = regexp.MustCompile(`(error|exception|fail)`)
tailLines = int64(100)
)
type LogAnalyzer struct {
}
func (LogAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
kind := "Log"
AnalyzerErrorsMetric.DeletePartialMatch(map[string]string{
"analyzer_name": kind,
})
// search all namespaces for pods that are not running
list, err := a.Client.GetClient().CoreV1().Pods(a.Namespace).List(a.Context, metav1.ListOptions{})
if err != nil {
return nil, err
}
var preAnalysis = map[string]common.PreAnalysis{}
// Iterate through each pod
for _, pod := range list.Items {
var failures []common.Failure
podName := pod.Name
podLogOptions := v1.PodLogOptions{
TailLines: &tailLines,
}
podLogs, err := a.Client.Client.CoreV1().Pods(pod.Namespace).GetLogs(podName, &podLogOptions).DoRaw(a.Context)
if err != nil {
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Error %s from Pod %s", err.Error(), pod.Name),
Sensitive: []common.Sensitive{
{
Unmasked: pod.Name,
Masked: util.MaskString(pod.Name),
},
},
})
} else {
rawlogs := string(podLogs)
if errorPattern.MatchString(strings.ToLower(rawlogs)) {
failures = append(failures, common.Failure{
Text: printErrorLines(pod.Name, pod.Namespace, rawlogs, errorPattern),
Sensitive: []common.Sensitive{
{
Unmasked: pod.Name,
Masked: util.MaskString(pod.Name),
},
},
})
}
}
if len(failures) > 0 {
preAnalysis[fmt.Sprintf("%s/%s", pod.Namespace, pod.Name)] = common.PreAnalysis{
FailureDetails: failures,
Pod: pod,
}
AnalyzerErrorsMetric.WithLabelValues(kind, pod.Name, pod.Namespace).Set(float64(len(failures)))
}
}
for key, value := range preAnalysis {
currentAnalysis := common.Result{
Kind: "Pod",
Name: key,
Error: value.FailureDetails,
}
parent, _ := util.GetParent(a.Client, value.Pod.ObjectMeta)
currentAnalysis.ParentObject = parent
a.Results = append(a.Results, currentAnalysis)
}
return a.Results, nil
}
func printErrorLines(podName, namespace, logs string, errorPattern *regexp.Regexp) string {
// Split the logs into lines
logLines := strings.Split(logs, "\n")
// Check each line for errors and print the lines containing errors
for _, line := range logLines {
if errorPattern.MatchString(strings.ToLower(line)) {
return line
}
}
return ""
}

View File

@@ -1,155 +0,0 @@
/*
Copyright 2023 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package analyzer
import (
"context"
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)
type MutatingWebhookAnalyzer struct{}
func (MutatingWebhookAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
kind := "MutatingWebhookConfiguration"
apiDoc := kubernetes.K8sApiReference{
Kind: kind,
ApiVersion: schema.GroupVersion{
Group: "apps",
Version: "v1",
},
OpenapiSchema: a.OpenapiSchema,
}
AnalyzerErrorsMetric.DeletePartialMatch(map[string]string{
"analyzer_name": kind,
})
mutatingWebhooks, err := a.Client.GetClient().AdmissionregistrationV1().MutatingWebhookConfigurations().List(context.Background(), v1.ListOptions{})
if err != nil {
return nil, err
}
var preAnalysis = map[string]common.PreAnalysis{}
for _, webhookConfig := range mutatingWebhooks.Items {
for _, webhook := range webhookConfig.Webhooks {
var failures []common.Failure
if webhook.ClientConfig.Service == nil {
continue
}
svc := webhook.ClientConfig.Service
// Get the service
service, err := a.Client.GetClient().CoreV1().Services(svc.Namespace).Get(context.Background(), svc.Name, v1.GetOptions{})
if err != nil {
// If the service is not found, we can't check the pods
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Service %s not found as mapped to by Mutating Webhook %s", svc.Name, webhook.Name),
KubernetesDoc: apiDoc.GetApiDocV2("spec.webhook.clientConfig.service"),
Sensitive: []common.Sensitive{
{
Unmasked: webhookConfig.Namespace,
Masked: util.MaskString(webhookConfig.Namespace),
},
{
Unmasked: svc.Name,
Masked: util.MaskString(svc.Name),
},
},
})
continue
}
// When Service selectors are empty we defer to service analyser
if len(service.Spec.Selector) == 0 {
continue
}
// Get pods within service
pods, err := a.Client.GetClient().CoreV1().Pods(svc.Namespace).List(context.Background(), v1.ListOptions{
LabelSelector: util.MapToString(service.Spec.Selector),
})
if err != nil {
return nil, err
}
if len(pods.Items) == 0 {
failures = append(failures, common.Failure{
Text: fmt.Sprintf("No active pods found within service %s as mapped to by Mutating Webhook %s", svc.Name, webhook.Name),
KubernetesDoc: apiDoc.GetApiDocV2("spec.webhook.clientConfig.service"),
Sensitive: []common.Sensitive{
{
Unmasked: webhookConfig.Namespace,
Masked: util.MaskString(webhookConfig.Namespace),
},
},
})
}
for _, pod := range pods.Items {
if pod.Status.Phase != "Running" {
doc := apiDoc.GetApiDocV2("spec.webhook")
failures = append(failures, common.Failure{
Text: fmt.Sprintf(
"Mutating Webhook (%s) is pointing to an inactive receiver pod (%s)",
webhook.Name,
pod.Name,
),
KubernetesDoc: doc,
Sensitive: []common.Sensitive{
{
Unmasked: webhookConfig.Namespace,
Masked: util.MaskString(webhookConfig.Namespace),
},
{
Unmasked: webhook.Name,
Masked: util.MaskString(webhook.Name),
},
{
Unmasked: pod.Name,
Masked: util.MaskString(pod.Name),
},
},
})
}
}
if len(failures) > 0 {
preAnalysis[fmt.Sprintf("%s/%s", webhookConfig.Namespace, webhook.Name)] = common.PreAnalysis{
MutatingWebhook: webhookConfig,
FailureDetails: failures,
}
AnalyzerErrorsMetric.WithLabelValues(kind, webhook.Name, webhookConfig.Namespace).Set(float64(len(failures)))
}
}
}
for key, value := range preAnalysis {
var currentAnalysis = common.Result{
Kind: kind,
Name: key,
Error: value.FailureDetails,
}
parent, _ := util.GetParent(a.Client, value.MutatingWebhook.ObjectMeta)
currentAnalysis.ParentObject = parent
a.Results = append(a.Results, currentAnalysis)
}
return a.Results, nil
}

View File

@@ -17,10 +17,8 @@ import (
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)
type NetworkPolicyAnalyzer struct{}
@@ -28,14 +26,6 @@ type NetworkPolicyAnalyzer struct{}
func (NetworkPolicyAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
kind := "NetworkPolicy"
apiDoc := kubernetes.K8sApiReference{
Kind: kind,
ApiVersion: schema.GroupVersion{
Group: "networking",
Version: "v1",
},
OpenapiSchema: a.OpenapiSchema,
}
AnalyzerErrorsMetric.DeletePartialMatch(map[string]string{
"analyzer_name": kind,
@@ -55,11 +45,8 @@ func (NetworkPolicyAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error)
// Check if policy allows traffic to all pods in the namespace
if len(policy.Spec.PodSelector.MatchLabels) == 0 {
doc := apiDoc.GetApiDocV2("spec.podSelector.matchLabels")
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Network policy allows traffic to all pods: %s", policy.Name),
KubernetesDoc: doc,
Text: fmt.Sprintf("Network policy allows traffic to all pods: %s", policy.Name),
Sensitive: []common.Sensitive{
{
Unmasked: policy.Name,

View File

@@ -58,7 +58,7 @@ func (NodeAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
}
if len(failures) > 0 {
preAnalysis[node.Name] = common.PreAnalysis{
preAnalysis[fmt.Sprintf("%s", node.Name)] = common.PreAnalysis{
Node: node,
FailureDetails: failures,
}

View File

@@ -17,10 +17,8 @@ import (
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)
type PdbAnalyzer struct{}
@@ -28,14 +26,6 @@ type PdbAnalyzer struct{}
func (PdbAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
kind := "PodDisruptionBudget"
apiDoc := kubernetes.K8sApiReference{
Kind: kind,
ApiVersion: schema.GroupVersion{
Group: "policy",
Version: "v1",
},
OpenapiSchema: a.OpenapiSchema,
}
AnalyzerErrorsMetric.DeletePartialMatch(map[string]string{
"analyzer_name": kind,
@@ -50,19 +40,17 @@ func (PdbAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
for _, pdb := range list.Items {
var failures []common.Failure
if pdb.Status.Conditions[0].Type == "DisruptionAllowed" && pdb.Status.Conditions[0].Status == "False" {
var doc string
if pdb.Spec.MaxUnavailable != nil {
doc = apiDoc.GetApiDocV2("spec.maxUnavailable")
}
if pdb.Spec.MinAvailable != nil {
doc = apiDoc.GetApiDocV2("spec.minAvailable")
}
if pdb.Spec.Selector != nil && pdb.Spec.Selector.MatchLabels != nil {
evt, err := FetchLatestEvent(a.Context, a.Client, pdb.Namespace, pdb.Name)
if err != nil || evt == nil {
continue
}
if evt.Reason == "NoPods" && evt.Message != "" {
if pdb.Spec.Selector != nil {
for k, v := range pdb.Spec.Selector.MatchLabels {
failures = append(failures, common.Failure{
Text: fmt.Sprintf("%s, expected pdb pod label %s=%s", pdb.Status.Conditions[0].Reason, k, v),
KubernetesDoc: doc,
Text: fmt.Sprintf("%s, expected label %s=%s", evt.Message, k, v),
Sensitive: []common.Sensitive{
{
Unmasked: k,
@@ -75,6 +63,17 @@ func (PdbAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
},
})
}
for _, v := range pdb.Spec.Selector.MatchExpressions {
failures = append(failures, common.Failure{
Text: fmt.Sprintf("%s, expected expression %s", evt.Message, v),
Sensitive: []common.Sensitive{},
})
}
} else {
failures = append(failures, common.Failure{
Text: fmt.Sprintf("%s, selector is nil", evt.Message),
Sensitive: []common.Sensitive{},
})
}
}

View File

@@ -59,16 +59,15 @@ func (PodAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
// Check through container status to check for crashes or unready
for _, containerStatus := range pod.Status.ContainerStatuses {
if containerStatus.State.Waiting != nil {
if isErrorReason(containerStatus.State.Waiting.Reason) && containerStatus.State.Waiting.Message != "" {
failures = append(failures, common.Failure{
Text: containerStatus.State.Waiting.Message,
Sensitive: []common.Sensitive{},
})
if containerStatus.State.Waiting.Reason == "CrashLoopBackOff" || containerStatus.State.Waiting.Reason == "ImagePullBackOff" {
if containerStatus.State.Waiting.Message != "" {
failures = append(failures, common.Failure{
Text: containerStatus.State.Waiting.Message,
Sensitive: []common.Sensitive{},
})
}
}
// This represents a container that is still being created or blocked due to conditions such as OOMKilled
if containerStatus.State.Waiting.Reason == "ContainerCreating" && pod.Status.Phase == "Pending" {
@@ -84,17 +83,9 @@ func (PodAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
})
}
}
// This represents container that is in CrashLoopBackOff state due to conditions such as OOMKilled
if containerStatus.State.Waiting.Reason == "CrashLoopBackOff" {
failures = append(failures, common.Failure{
Text: fmt.Sprintf("the last termination reason is %s container=%s pod=%s", containerStatus.LastTerminationState.Terminated.Reason, containerStatus.Name, pod.Name),
Sensitive: []common.Sensitive{},
})
}
} else {
// when pod is Running but its ReadinessProbe fails
if !containerStatus.Ready && pod.Status.Phase == "Running" {
if containerStatus.Ready == false && pod.Status.Phase == "Running" {
// parse the event log and append details
evt, err := FetchLatestEvent(a.Context, a.Client, pod.Namespace, pod.Name)
if err != nil || evt == nil {
@@ -134,16 +125,3 @@ func (PodAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
return a.Results, nil
}
func isErrorReason(reason string) bool {
failureReasons := []string{
"CrashLoopBackOff", "ImagePullBackOff", "CreateContainerConfigError", "PreCreateHookError", "CreateContainerError", "PreStartHookError", "RunContainerError", "ImageInspectError", "ErrImagePull", "ErrImageNeverPull", "InvalidImageName",
}
for _, r := range failureReasons {
if r == reason {
return true
}
}
return false
}

View File

@@ -17,13 +17,9 @@ import (
"fmt"
"github.com/fatih/color"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/leaderelection/resourcelock"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
"k8s.io/apimachinery/pkg/runtime/schema"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type ServiceAnalyzer struct{}
@@ -31,14 +27,6 @@ type ServiceAnalyzer struct{}
func (ServiceAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
kind := "Service"
apiDoc := kubernetes.K8sApiReference{
Kind: kind,
ApiVersion: schema.GroupVersion{
Group: "",
Version: "v1",
},
OpenapiSchema: a.OpenapiSchema,
}
AnalyzerErrorsMetric.DeletePartialMatch(map[string]string{
"analyzer_name": kind,
@@ -57,10 +45,6 @@ func (ServiceAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
// Check for empty service
if len(ep.Subsets) == 0 {
if _, ok := ep.Annotations[resourcelock.LeaderElectionRecordAnnotationKey]; ok {
continue
}
svc, err := a.Client.GetClient().CoreV1().Services(ep.Namespace).Get(a.Context, ep.Name, metav1.GetOptions{})
if err != nil {
color.Yellow("Service %s/%s does not exist", ep.Namespace, ep.Name)
@@ -68,11 +52,8 @@ func (ServiceAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
}
for k, v := range svc.Spec.Selector {
doc := apiDoc.GetApiDocV2("spec.selector")
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Service has no endpoints, expected label %s=%s", k, v),
KubernetesDoc: doc,
Text: fmt.Sprintf("Service has no endpoints, expected label %s=%s", k, v),
Sensitive: []common.Sensitive{
{
Unmasked: k,
@@ -91,20 +72,14 @@ func (ServiceAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
// Check through container status to check for crashes
for _, epSubset := range ep.Subsets {
apiDoc.Kind = "Endpoints"
if len(epSubset.NotReadyAddresses) > 0 {
for _, addresses := range epSubset.NotReadyAddresses {
count++
pods = append(pods, addresses.TargetRef.Kind+"/"+addresses.TargetRef.Name)
}
doc := apiDoc.GetApiDocV2("subsets.notReadyAddresses")
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Service has not ready endpoints, pods: %s, expected %d", pods, count),
KubernetesDoc: doc,
Sensitive: []common.Sensitive{},
Text: fmt.Sprintf("Service has not ready endpoints, pods: %s, expected %d", pods, count),
Sensitive: []common.Sensitive{},
})
}
}

View File

@@ -17,10 +17,8 @@ import (
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)
type StatefulSetAnalyzer struct{}
@@ -28,14 +26,6 @@ type StatefulSetAnalyzer struct{}
func (StatefulSetAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
kind := "StatefulSet"
apiDoc := kubernetes.K8sApiReference{
Kind: kind,
ApiVersion: schema.GroupVersion{
Group: "apps",
Version: "v1",
},
OpenapiSchema: a.OpenapiSchema,
}
AnalyzerErrorsMetric.DeletePartialMatch(map[string]string{
"analyzer_name": kind,
@@ -54,15 +44,8 @@ func (StatefulSetAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
serviceName := sts.Spec.ServiceName
_, err := a.Client.GetClient().CoreV1().Services(sts.Namespace).Get(a.Context, serviceName, metav1.GetOptions{})
if err != nil {
doc := apiDoc.GetApiDocV2("spec.serviceName")
failures = append(failures, common.Failure{
Text: fmt.Sprintf(
"StatefulSet uses the service %s/%s which does not exist.",
sts.Namespace,
serviceName,
),
KubernetesDoc: doc,
Text: fmt.Sprintf("StatefulSet uses the service %s/%s which does not exist.", sts.Namespace, serviceName),
Sensitive: []common.Sensitive{
{
Unmasked: sts.Namespace,

View File

@@ -1,153 +0,0 @@
/*
Copyright 2023 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package analyzer
import (
"context"
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)
type ValidatingWebhookAnalyzer struct{}
func (ValidatingWebhookAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
kind := "ValidatingWebhookConfgiguration"
apiDoc := kubernetes.K8sApiReference{
Kind: kind,
ApiVersion: schema.GroupVersion{
Group: "apps",
Version: "v1",
},
OpenapiSchema: a.OpenapiSchema,
}
AnalyzerErrorsMetric.DeletePartialMatch(map[string]string{
"analyzer_name": kind,
})
validatingWebhooks, err := a.Client.GetClient().AdmissionregistrationV1().ValidatingWebhookConfigurations().List(context.Background(), v1.ListOptions{})
if err != nil {
return nil, err
}
var preAnalysis = map[string]common.PreAnalysis{}
for _, webhookConfig := range validatingWebhooks.Items {
for _, webhook := range webhookConfig.Webhooks {
var failures []common.Failure
if webhook.ClientConfig.Service == nil {
continue
}
svc := webhook.ClientConfig.Service
// Get the service
service, err := a.Client.GetClient().CoreV1().Services(svc.Namespace).Get(context.Background(), svc.Name, v1.GetOptions{})
if err != nil {
// If the service is not found, we can't check the pods
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Service %s not found as mapped to by Validating Webhook %s", svc.Name, webhook.Name),
KubernetesDoc: apiDoc.GetApiDocV2("spec.webhook.clientConfig.service"),
Sensitive: []common.Sensitive{
{
Unmasked: webhookConfig.Namespace,
Masked: util.MaskString(webhookConfig.Namespace),
},
{
Unmasked: svc.Name,
Masked: util.MaskString(svc.Name),
},
},
})
continue
}
// When Service selectors are empty we defer to service analyser
if len(service.Spec.Selector) == 0 {
continue
}
// Get pods within service
pods, err := a.Client.GetClient().CoreV1().Pods(svc.Namespace).List(context.Background(), v1.ListOptions{
LabelSelector: util.MapToString(service.Spec.Selector),
})
if err != nil {
return nil, err
}
if len(pods.Items) == 0 {
failures = append(failures, common.Failure{
Text: fmt.Sprintf("No active pods found within service %s as mapped to by Validating Webhook %s", svc.Name, webhook.Name),
KubernetesDoc: apiDoc.GetApiDocV2("spec.webhook.clientConfig.service"),
Sensitive: []common.Sensitive{
{
Unmasked: webhookConfig.Namespace,
Masked: util.MaskString(webhookConfig.Namespace),
},
},
})
}
for _, pod := range pods.Items {
if pod.Status.Phase != "Running" {
doc := apiDoc.GetApiDocV2("spec.webhook")
failures = append(failures, common.Failure{
Text: fmt.Sprintf(
"Validating Webhook (%s) is pointing to an inactive receiver pod (%s)",
webhook.Name,
pod.Name,
),
KubernetesDoc: doc,
Sensitive: []common.Sensitive{
{
Unmasked: webhookConfig.Namespace,
Masked: util.MaskString(webhookConfig.Namespace),
},
{
Unmasked: webhook.Name,
Masked: util.MaskString(webhook.Name),
},
{
Unmasked: pod.Name,
Masked: util.MaskString(pod.Name),
},
},
})
}
}
if len(failures) > 0 {
preAnalysis[fmt.Sprintf("%s/%s", webhookConfig.Namespace, webhook.Name)] = common.PreAnalysis{
ValidatingWebhook: webhookConfig,
FailureDetails: failures,
}
AnalyzerErrorsMetric.WithLabelValues(kind, webhook.Name, webhookConfig.Namespace).Set(float64(len(failures)))
}
}
}
for key, value := range preAnalysis {
var currentAnalysis = common.Result{
Kind: kind,
Name: key,
Error: value.FailureDetails,
}
parent, _ := util.GetParent(a.Client, value.ValidatingWebhook.ObjectMeta)
currentAnalysis.ParentObject = parent
a.Results = append(a.Results, currentAnalysis)
}
return a.Results, nil
}

View File

@@ -1,155 +0,0 @@
package cache
import (
"bytes"
"context"
"fmt"
"log"
"strings"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob"
)
// Generate ICache implementation
type AzureCache struct {
ctx context.Context
noCache bool
containerName string
session *azblob.Client
}
type AzureCacheConfiguration struct {
StorageAccount string `mapstructure:"storageaccount" yaml:"storageaccount,omitempty"`
ContainerName string `mapstructure:"container" yaml:"container,omitempty"`
}
func (s *AzureCache) Configure(cacheInfo CacheProvider) error {
s.ctx = context.Background()
if cacheInfo.Azure.ContainerName == "" {
log.Fatal("Azure Container name not configured")
}
if cacheInfo.Azure.StorageAccount == "" {
log.Fatal("Azure Storage account not configured")
}
// We assume that Storage account is already in place
blobUrl := fmt.Sprintf("https://%s.blob.core.windows.net/", cacheInfo.Azure.StorageAccount)
credential, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
log.Fatal(err)
}
client, err := azblob.NewClient(blobUrl, credential, nil)
if err != nil {
log.Fatal(err)
}
// Try to create the blob container
_, err = client.CreateContainer(s.ctx, cacheInfo.Azure.ContainerName, nil)
if err != nil {
// TODO: Maybe there is a better way to check this?
// docs: https://pkg.go.dev/github.com/Azure/azure-storage-blob-go/azblob
if strings.Contains(err.Error(), "ContainerAlreadyExists") {
// do nothing
} else {
return err
}
}
s.containerName = cacheInfo.Azure.ContainerName
s.session = client
return nil
}
func (s *AzureCache) Store(key string, data string) error {
// Store the object as a new file in the Azure blob storage with data as the content
cacheData := []byte(data)
_, err := s.session.UploadBuffer(s.ctx, s.containerName, key, cacheData, &azblob.UploadBufferOptions{})
return err
}
func (s *AzureCache) Load(key string) (string, error) {
// Load blob file contents
load, err := s.session.DownloadStream(s.ctx, s.containerName, key, nil)
if err != nil {
return "", err
}
data := bytes.Buffer{}
retryReader := load.NewRetryReader(s.ctx, &azblob.RetryReaderOptions{})
_, err = data.ReadFrom(retryReader)
if err != nil {
return "", err
}
if err := retryReader.Close(); err != nil {
return "", err
}
return data.String(), nil
}
func (s *AzureCache) List() ([]CacheObjectDetails, error) {
// List the files in the blob containerName
files := []CacheObjectDetails{}
pager := s.session.NewListBlobsFlatPager(s.containerName, &azblob.ListBlobsFlatOptions{
Include: azblob.ListBlobsInclude{Snapshots: false, Versions: false},
})
for pager.More() {
resp, err := pager.NextPage(s.ctx)
if err != nil {
return nil, err
}
for _, blob := range resp.Segment.BlobItems {
files = append(files, CacheObjectDetails{
Name: *blob.Name,
UpdatedAt: *blob.Properties.LastModified,
})
}
}
return files, nil
}
func (s *AzureCache) Remove(key string) error {
_, err := s.session.DeleteBlob(s.ctx, s.containerName, key, &blob.DeleteOptions{})
if err != nil {
return err
}
return nil
}
func (s *AzureCache) Exists(key string) bool {
// Check if the object exists in the blob storage
pager := s.session.NewListBlobsFlatPager(s.containerName, &azblob.ListBlobsFlatOptions{
Include: azblob.ListBlobsInclude{Snapshots: false, Versions: false},
})
for pager.More() {
resp, err := pager.NextPage(s.ctx)
if err != nil {
return false
}
for _, blob := range resp.Segment.BlobItems {
if *blob.Name == key {
return true
}
}
}
return false
}
func (s *AzureCache) IsCacheDisabled() bool {
return s.noCache
}
func (s *AzureCache) GetName() string {
return "azure"
}
func (s *AzureCache) DisableCache() {
s.noCache = true
}

120
pkg/cache/cache.go vendored
View File

@@ -1,128 +1,14 @@
package cache
import (
"fmt"
"github.com/spf13/viper"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
var (
types = []ICache{
&AzureCache{},
&FileBasedCache{},
&GCSCache{},
&S3Cache{},
}
)
type ICache interface {
Configure(cacheInfo CacheProvider) error
Store(key string, data string) error
Load(key string) (string, error)
List() ([]CacheObjectDetails, error)
Remove(key string) error
Exists(key string) bool
IsCacheDisabled() bool
GetName() string
DisableCache()
}
func New(cacheType string) ICache {
for _, t := range types {
if cacheType == t.GetName() {
return t
}
func New(noCache bool) ICache {
return &FileBasedCache{
noCache: noCache,
}
return &FileBasedCache{}
}
func ParseCacheConfiguration() (CacheProvider, error) {
var cacheInfo CacheProvider
err := viper.UnmarshalKey("cache", &cacheInfo)
if err != nil {
return cacheInfo, err
}
return cacheInfo, nil
}
func NewCacheProvider(cacheType, bucketname, region, storageAccount, containerName, projectId string) (CacheProvider, error) {
cProvider := CacheProvider{}
switch {
case cacheType == "azure":
cProvider.Azure.ContainerName = containerName
cProvider.Azure.StorageAccount = storageAccount
case cacheType == "gcs":
cProvider.GCS.BucketName = bucketname
cProvider.GCS.ProjectId = projectId
cProvider.GCS.Region = region
case cacheType == "s3":
cProvider.S3.BucketName = bucketname
cProvider.S3.Region = region
default:
return CacheProvider{}, status.Error(codes.Internal, fmt.Sprintf("%s is not a valid option", cacheType))
}
cache := New(cacheType)
err := cache.Configure(cProvider)
if err != nil {
return CacheProvider{}, err
}
return cProvider, nil
}
// If we have set a remote cache, return the remote cache configuration
func GetCacheConfiguration() (ICache, error) {
cacheInfo, err := ParseCacheConfiguration()
if err != nil {
return nil, err
}
var cache ICache
switch {
case cacheInfo.GCS != GCSCacheConfiguration{}:
cache = &GCSCache{}
case cacheInfo.Azure != AzureCacheConfiguration{}:
cache = &AzureCache{}
case cacheInfo.S3 != S3CacheConfiguration{}:
cache = &S3Cache{}
default:
cache = &FileBasedCache{}
}
err_config := cache.Configure(cacheInfo)
return cache, err_config
}
func AddRemoteCache(cacheInfo CacheProvider) error {
viper.Set("cache", cacheInfo)
err := viper.WriteConfig()
if err != nil {
return err
}
return nil
}
func RemoveRemoteCache() error {
var cacheInfo CacheProvider
err := viper.UnmarshalKey("cache", &cacheInfo)
if err != nil {
return status.Error(codes.Internal, "cache unmarshal")
}
cacheInfo = CacheProvider{}
viper.Set("cache", cacheInfo)
err = viper.WriteConfig()
if err != nil {
return status.Error(codes.Internal, "unable to write config")
}
return nil
}

View File

@@ -15,40 +15,10 @@ type FileBasedCache struct {
noCache bool
}
func (f *FileBasedCache) Configure(cacheInfo CacheProvider) error {
return nil
}
func (f *FileBasedCache) IsCacheDisabled() bool {
return f.noCache
}
func (*FileBasedCache) List() ([]CacheObjectDetails, error) {
path, err := xdg.CacheFile("k8sgpt")
if err != nil {
return nil, err
}
files, err := os.ReadDir(path)
if err != nil {
return nil, err
}
var result []CacheObjectDetails
for _, file := range files {
info, err := file.Info()
if err != nil {
return nil, err
}
result = append(result, CacheObjectDetails{
Name: file.Name(),
UpdatedAt: info.ModTime(),
})
}
return result, nil
}
func (*FileBasedCache) Exists(key string) bool {
path, err := xdg.CacheFile(filepath.Join("k8sgpt", key))
@@ -83,20 +53,6 @@ func (*FileBasedCache) Load(key string) (string, error) {
return string(data), nil
}
func (*FileBasedCache) Remove(key string) error {
path, err := xdg.CacheFile(filepath.Join("k8sgpt", key))
if err != nil {
return err
}
if err := os.Remove(path); err != nil {
return err
}
return nil
}
func (*FileBasedCache) Store(key string, data string) error {
path, err := xdg.CacheFile(filepath.Join("k8sgpt", key))
@@ -106,11 +62,3 @@ func (*FileBasedCache) Store(key string, data string) error {
return os.WriteFile(path, []byte(data), 0600)
}
func (s *FileBasedCache) GetName() string {
return "file"
}
func (s *FileBasedCache) DisableCache() {
s.noCache = true
}

133
pkg/cache/gcs_based.go vendored
View File

@@ -1,133 +0,0 @@
package cache
import (
"context"
"io"
"log"
"cloud.google.com/go/storage"
"google.golang.org/api/iterator"
)
type GCSCache struct {
ctx context.Context
noCache bool
bucketName string
projectId string
region string
session *storage.Client
}
type GCSCacheConfiguration struct {
ProjectId string `mapstructure:"projectid" yaml:"projectid,omitempty"`
Region string `mapstructure:"region" yaml:"region,omitempty"`
BucketName string `mapstructure:"bucketname" yaml:"bucketname,omitempty"`
}
func (s *GCSCache) Configure(cacheInfo CacheProvider) error {
s.ctx = context.Background()
if cacheInfo.GCS.BucketName == "" {
log.Fatal("Bucket name not configured")
}
if cacheInfo.GCS.Region == "" {
log.Fatal("Region not configured")
}
if cacheInfo.GCS.ProjectId == "" {
log.Fatal("ProjectID not configured")
}
s.bucketName = cacheInfo.GCS.BucketName
s.projectId = cacheInfo.GCS.ProjectId
s.region = cacheInfo.GCS.Region
storageClient, err := storage.NewClient(s.ctx)
if err != nil {
log.Fatal(err)
}
_, err = storageClient.Bucket(s.bucketName).Attrs(s.ctx)
if err == storage.ErrBucketNotExist {
err = storageClient.Bucket(s.bucketName).Create(s.ctx, s.projectId, &storage.BucketAttrs{
Location: s.region,
})
if err != nil {
return err
}
}
s.session = storageClient
return nil
}
func (s *GCSCache) Store(key string, data string) error {
wc := s.session.Bucket(s.bucketName).Object(key).NewWriter(s.ctx)
if _, err := wc.Write([]byte(data)); err != nil {
return err
}
if err := wc.Close(); err != nil {
return err
}
return nil
}
func (s *GCSCache) Load(key string) (string, error) {
reader, err := s.session.Bucket(s.bucketName).Object(key).NewReader(s.ctx)
if err != nil {
return "", err
}
defer reader.Close()
data, err := io.ReadAll(reader)
if err != nil {
return "", err
}
return string(data), nil
}
func (s *GCSCache) Remove(key string) error {
bucketClient := s.session.Bucket(s.bucketName)
obj := bucketClient.Object(key)
if err := obj.Delete(s.ctx); err != nil {
return err
}
return nil
}
func (s *GCSCache) List() ([]CacheObjectDetails, error) {
var files []CacheObjectDetails
items := s.session.Bucket(s.bucketName).Objects(s.ctx, nil)
for {
attrs, err := items.Next()
if err == iterator.Done {
break
}
if err != nil {
return nil, err
}
files = append(files, CacheObjectDetails{
Name: attrs.Name,
UpdatedAt: attrs.Updated,
})
}
return files, nil
}
func (s *GCSCache) Exists(key string) bool {
obj := s.session.Bucket(s.bucketName).Object(key)
_, err := obj.Attrs(s.ctx)
return err == nil
}
func (s *GCSCache) IsCacheDisabled() bool {
return s.noCache
}
func (s *GCSCache) GetName() string {
return "gcs"
}
func (s *GCSCache) DisableCache() {
s.noCache = true
}

137
pkg/cache/s3_based.go vendored
View File

@@ -1,137 +0,0 @@
package cache
import (
"bytes"
"log"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
)
// Generate ICache implementation
type S3Cache struct {
noCache bool
bucketName string
session *s3.S3
}
type S3CacheConfiguration struct {
Region string `mapstructure:"region" yaml:"region,omitempty"`
BucketName string `mapstructure:"bucketname" yaml:"bucketname,omitempty"`
}
func (s *S3Cache) Configure(cacheInfo CacheProvider) error {
if cacheInfo.S3.BucketName == "" {
log.Fatal("Bucket name not configured")
}
if cacheInfo.S3.Region == "" {
log.Fatal("Region not configured")
}
s.bucketName = cacheInfo.S3.BucketName
sess := session.Must(session.NewSessionWithOptions(session.Options{
SharedConfigState: session.SharedConfigEnable,
Config: aws.Config{
Region: aws.String(cacheInfo.S3.Region),
},
}))
s3Client := s3.New(sess)
// Check if the bucket exists, if not create it
_, err := s3Client.HeadBucket(&s3.HeadBucketInput{
Bucket: aws.String(cacheInfo.S3.BucketName),
})
if err != nil {
_, err = s3Client.CreateBucket(&s3.CreateBucketInput{
Bucket: aws.String(cacheInfo.S3.BucketName),
})
if err != nil {
return err
}
}
s.session = s3Client
return nil
}
func (s *S3Cache) Store(key string, data string) error {
// Store the object as a new file in the bucket with data as the content
_, err := s.session.PutObject(&s3.PutObjectInput{
Body: aws.ReadSeekCloser(bytes.NewReader([]byte(data))),
Bucket: aws.String(s.bucketName),
Key: aws.String(key),
})
return err
}
func (s *S3Cache) Remove(key string) error {
_, err := s.session.DeleteObject(&s3.DeleteObjectInput{
Bucket: &s.bucketName,
Key: aws.String(key),
})
if err != nil {
return err
}
return nil
}
func (s *S3Cache) Load(key string) (string, error) {
// Retrieve the object from the bucket and load it into a string
result, err := s.session.GetObject(&s3.GetObjectInput{
Bucket: aws.String(s.bucketName),
Key: aws.String(key),
})
if err != nil {
return "", err
}
buf := new(bytes.Buffer)
_, err_read := buf.ReadFrom(result.Body)
result.Body.Close()
return buf.String(), err_read
}
func (s *S3Cache) List() ([]CacheObjectDetails, error) {
// List the files in the bucket
result, err := s.session.ListObjectsV2(&s3.ListObjectsV2Input{Bucket: aws.String(s.bucketName)})
if err != nil {
return nil, err
}
var keys []CacheObjectDetails
for _, item := range result.Contents {
keys = append(keys, CacheObjectDetails{
Name: *item.Key,
UpdatedAt: *item.LastModified,
})
}
return keys, nil
}
func (s *S3Cache) Exists(key string) bool {
// Check if the object exists in the bucket
_, err := s.session.HeadObject(&s3.HeadObjectInput{
Bucket: aws.String(s.bucketName),
Key: aws.String(key),
})
return err == nil
}
func (s *S3Cache) IsCacheDisabled() bool {
return s.noCache
}
func (s *S3Cache) GetName() string {
return "s3"
}
func (s *S3Cache) DisableCache() {
s.noCache = true
}

14
pkg/cache/types.go vendored
View File

@@ -1,14 +0,0 @@
package cache
import "time"
type CacheProvider struct {
GCS GCSCacheConfiguration `mapstructucre:"gcs" yaml:"gcs,omitempty"`
Azure AzureCacheConfiguration `mapstructucre:"azure" yaml:"azure,omitempty"`
S3 S3CacheConfiguration `mapstructucre:"s3" yaml:"s3,omitempty"`
}
type CacheObjectDetails struct {
Name string
UpdatedAt time.Time
}

View File

@@ -17,16 +17,13 @@ import (
"context"
trivy "github.com/aquasecurity/trivy-operator/pkg/apis/aquasecurity/v1alpha1"
openapi_v2 "github.com/google/gnostic/openapiv2"
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
regv1 "k8s.io/api/admissionregistration/v1"
appsv1 "k8s.io/api/apps/v1"
autov1 "k8s.io/api/autoscaling/v1"
v1 "k8s.io/api/core/v1"
networkv1 "k8s.io/api/networking/v1"
policyv1 "k8s.io/api/policy/v1"
gtwapi "sigs.k8s.io/gateway-api/apis/v1"
)
type IAnalyzer interface {
@@ -34,13 +31,12 @@ type IAnalyzer interface {
}
type Analyzer struct {
Client *kubernetes.Client
Context context.Context
Namespace string
AIClient ai.IAI
PreAnalysis map[string]PreAnalysis
Results []Result
OpenapiSchema *openapi_v2.Document
Client *kubernetes.Client
Context context.Context
Namespace string
AIClient ai.IAI
PreAnalysis map[string]PreAnalysis
Results []Result
}
type PreAnalysis struct {
@@ -56,14 +52,8 @@ type PreAnalysis struct {
StatefulSet appsv1.StatefulSet
NetworkPolicy networkv1.NetworkPolicy
Node v1.Node
ValidatingWebhook regv1.ValidatingWebhookConfiguration
MutatingWebhook regv1.MutatingWebhookConfiguration
GatewayClass gtwapi.GatewayClass
Gateway gtwapi.Gateway
HTTPRoute gtwapi.HTTPRoute
// Integrations
TrivyVulnerabilityReport trivy.VulnerabilityReport
TrivyConfigAuditReport trivy.ConfigAuditReport
}
type Result struct {
@@ -75,9 +65,8 @@ type Result struct {
}
type Failure struct {
Text string
KubernetesDoc string
Sensitive []Sensitive
Text string
Sensitive []Sensitive
}
type Sensitive struct {

View File

@@ -15,10 +15,11 @@ package integration
import (
"errors"
"fmt"
"os"
"strings"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/integration/prometheus"
"github.com/k8sgpt-ai/k8sgpt/pkg/integration/trivy"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
"github.com/spf13/viper"
@@ -31,12 +32,10 @@ type IIntegration interface {
UnDeploy(namespace string) error
//
AddAnalyzer(*map[string]common.IAnalyzer)
// RemoveAnalyzer removes an analyzer from the cluster
RemoveAnalyzer() error
GetAnalyzerName() []string
// An integration must keep record of its deployed namespace (if not using --no-install)
GetNamespace() (string, error)
OwnsAnalyzer(string) bool
GetAnalyzerName() string
IsActivate() bool
}
@@ -45,8 +44,7 @@ type Integration struct {
}
var integrations = map[string]IIntegration{
"trivy": trivy.NewTrivy(),
"prometheus": prometheus.NewPrometheus(),
"trivy": trivy.NewTrivy(),
}
func NewIntegration() *Integration {
@@ -68,37 +66,33 @@ func (*Integration) Get(name string) (IIntegration, error) {
return integrations[name], nil
}
func (i *Integration) AnalyzerByIntegration(input string) (string, error) {
for _, name := range i.List() {
if integ, err := i.Get(name); err == nil {
if integ.OwnsAnalyzer(input) {
return name, nil
}
}
}
return "", errors.New("analyzerbyintegration: no matches found")
}
func (*Integration) Activate(name string, namespace string, activeFilters []string, skipInstall bool) error {
func (*Integration) Activate(name string, namespace string) error {
if _, ok := integrations[name]; !ok {
return errors.New("integration not found")
}
if !skipInstall {
if err := integrations[name].Deploy(namespace); err != nil {
return err
}
// Update filters
activeFilters := viper.GetStringSlice("active_filters")
mergedFilters := append(activeFilters, integrations[name].GetAnalyzerName())
uniqueFilters, dupplicatedFilters := util.RemoveDuplicates(mergedFilters)
// Verify dupplicate
if len(dupplicatedFilters) != 0 {
color.Red("Integration already activated : %s", strings.Join(dupplicatedFilters, ", "))
os.Exit(1)
}
mergedFilters := activeFilters
mergedFilters = append(mergedFilters, integrations[name].GetAnalyzerName()...)
uniqueFilters, _ := util.RemoveDuplicates(mergedFilters)
viper.Set("active_filters", uniqueFilters)
if err := viper.WriteConfig(); err != nil {
return fmt.Errorf("error writing config file: %s", err.Error())
if err := integrations[name].Deploy(namespace); err != nil {
return err
}
if err := viper.WriteConfig(); err != nil {
color.Red("Error writing config file: %s", err.Error())
os.Exit(1)
}
return nil
@@ -111,14 +105,19 @@ func (*Integration) Deactivate(name string, namespace string) error {
activeFilters := viper.GetStringSlice("active_filters")
// Update filters and remove the specific filters for the integration
for _, filter := range integrations[name].GetAnalyzerName() {
for x, af := range activeFilters {
if af == filter {
activeFilters = append(activeFilters[:x], activeFilters[x+1:]...)
}
// Update filters
// This might be a bad idea, but we cannot reference analyzer here
foundFilter := false
for i, v := range activeFilters {
if v == integrations[name].GetAnalyzerName() {
foundFilter = true
activeFilters = append(activeFilters[:i], activeFilters[i+1:]...)
break
}
}
if !foundFilter {
color.Red("Ingregation %s does not exist in configuration file. Please use k8sgpt integration add.", name)
os.Exit(1)
}
if err := integrations[name].UnDeploy(namespace); err != nil {
@@ -128,8 +127,8 @@ func (*Integration) Deactivate(name string, namespace string) error {
viper.Set("active_filters", activeFilters)
if err := viper.WriteConfig(); err != nil {
return fmt.Errorf("error writing config file: %s", err.Error())
color.Red("Error writing config file: %s", err.Error())
os.Exit(1)
}
return nil

Some files were not shown because too many files have changed in this diff Show More