Compare commits
333 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb32a0f2bc | ||
|
|
ec08cac214 | ||
|
|
dca5b4710d | ||
|
|
b1c791a396 | ||
|
|
b780105807 | ||
|
|
16469c01c9 | ||
|
|
cf1e243708 | ||
|
|
b5853de8a6 | ||
|
|
58d182e94f | ||
|
|
5b27c3e352 | ||
|
|
3e3f6a903a | ||
|
|
12146bf356 | ||
|
|
beaa53251c | ||
|
|
e2bb567d2f | ||
|
|
24132c2d87 | ||
|
|
11e568c6c2 | ||
|
|
9971699fcf | ||
|
|
531fa79ed6 | ||
|
|
2aa31bc66d | ||
|
|
84df3640bc | ||
|
|
73682717ed | ||
|
|
45312788c3 | ||
|
|
c78c4f0cb6 | ||
|
|
d365886753 | ||
|
|
4408110b1a | ||
|
|
c4925b2170 | ||
|
|
66ebb88efe | ||
|
|
1a554ae840 | ||
|
|
19e502a841 | ||
|
|
fdb2934e8f | ||
|
|
45ebad7b4d | ||
|
|
6a665f05d7 | ||
|
|
d359caaab6 | ||
|
|
9dadd186c8 | ||
|
|
3bff9cbe7b | ||
|
|
05f444dec1 | ||
|
|
bb21ce80c7 | ||
|
|
48486e9627 | ||
|
|
120027e3cb | ||
|
|
9c518badf5 | ||
|
|
8ab26d96ce | ||
|
|
4978fe2523 | ||
|
|
ccef7f6170 | ||
|
|
188a8a2cd5 | ||
|
|
37721b5dd7 | ||
|
|
16b229d547 | ||
|
|
901c5ec188 | ||
|
|
40133adaed | ||
|
|
0136b8f543 | ||
|
|
f4b361aed6 | ||
|
|
f1a7801e9e | ||
|
|
4af0ad0303 | ||
|
|
a77bd41048 | ||
|
|
63a226065c | ||
|
|
0e7219a36a | ||
|
|
7e73f8afbc | ||
|
|
23ac52d5ff | ||
|
|
c977528ec7 | ||
|
|
b5facd64a3 | ||
|
|
1d7360c0ae | ||
|
|
184d148108 | ||
|
|
3ebc86772d | ||
|
|
c5c198e3d6 | ||
|
|
539ca3b78f | ||
|
|
2a34ff24d1 | ||
|
|
6d3038b0e8 | ||
|
|
3f36a44415 | ||
|
|
923a8c13c0 | ||
|
|
95c8cc0afb | ||
|
|
c5a8c46298 | ||
|
|
2494946dc8 | ||
|
|
f11d3149b2 | ||
|
|
20e6bd816f | ||
|
|
85ce557681 | ||
|
|
87c8bcea4b | ||
|
|
4d4e33bea9 | ||
|
|
9597002723 | ||
|
|
bcd058c3b0 | ||
|
|
69fe2db8ac | ||
|
|
ddeff9fae4 | ||
|
|
aa9e6a3549 | ||
|
|
e1a42ff3bc | ||
|
|
901ffb8df4 | ||
|
|
402e97d05e | ||
|
|
1da4b7c8f0 | ||
|
|
820e4755a5 | ||
|
|
cad605af46 | ||
|
|
9bd11feb01 | ||
|
|
7461a748f8 | ||
|
|
b4656f533b | ||
|
|
4bfc7f996c | ||
|
|
844ff1fc78 | ||
|
|
d6b7b818ae | ||
|
|
f55946d60e | ||
|
|
54caff837d | ||
|
|
3277b2ad4b | ||
|
|
f60736035b | ||
|
|
1b80b0ce95 | ||
|
|
2472da1673 | ||
|
|
ab064b940c | ||
|
|
6481590b29 | ||
|
|
0325724658 | ||
|
|
f76b572654 | ||
|
|
abfb58432f | ||
|
|
d58e002d7d | ||
|
|
ac2937c821 | ||
|
|
6c5a0628e4 | ||
|
|
598ef22e57 | ||
|
|
95787f2854 | ||
|
|
230eace187 | ||
|
|
b491c9200e | ||
|
|
00d7a27ec1 | ||
|
|
241f1bd6df | ||
|
|
25890e6e38 | ||
|
|
3aabb4842d | ||
|
|
5de3b64098 | ||
|
|
5f73240a06 | ||
|
|
44d17c51ff | ||
|
|
2c81dadb4d | ||
|
|
067c3483e6 | ||
|
|
e65d9a6505 | ||
|
|
c24825b810 | ||
|
|
b6498ef269 | ||
|
|
133850f984 | ||
|
|
09984c245d | ||
|
|
1a7f45cc55 | ||
|
|
fc90dc865b | ||
|
|
632fc9a99f | ||
|
|
96d97cfa30 | ||
|
|
8f0a2fd41d | ||
|
|
5e17e66665 | ||
|
|
6f9f7b2b60 | ||
|
|
95ebc52a6b | ||
|
|
44d3613c1f | ||
|
|
0e5be89e5c | ||
|
|
13f63eda2a | ||
|
|
cc665ea4f3 | ||
|
|
aee83b74b2 | ||
|
|
e3b21ec5ec | ||
|
|
1a0ae1a086 | ||
|
|
610720a95c | ||
|
|
7910c9aa2c | ||
|
|
f8a53a5c03 | ||
|
|
81fcf8b5d4 | ||
|
|
2251321696 | ||
|
|
9672cea228 | ||
|
|
7f109cdcfa | ||
|
|
9802e82ff5 | ||
|
|
9acaec00c4 | ||
|
|
fe29361e33 | ||
|
|
f1479babba | ||
|
|
c2770f38a6 | ||
|
|
59897f330a | ||
|
|
aafac9345f | ||
|
|
227e1cd69f | ||
|
|
5bb91ff2c9 | ||
|
|
43b0d707e7 | ||
|
|
c3640744c5 | ||
|
|
3cc7aa56d8 | ||
|
|
7e8668a56b | ||
|
|
b52424a9b1 | ||
|
|
fba1a8ed8c | ||
|
|
8ae91ec744 | ||
|
|
cf9069ef57 | ||
|
|
5ee8499e3d | ||
|
|
153d38deb0 | ||
|
|
9d2bee9b7d | ||
|
|
70bec050d8 | ||
|
|
781ecb7aad | ||
|
|
c42221512b | ||
|
|
2e0db553f9 | ||
|
|
cc83fe19ba | ||
|
|
dc463334bc | ||
|
|
9eb96c495c | ||
|
|
ee83aa1e9e | ||
|
|
06e8532f88 | ||
|
|
1a3f299210 | ||
|
|
731e1520ec | ||
|
|
f844f7308e | ||
|
|
750a10d44c | ||
|
|
9dcab94546 | ||
|
|
ecd7790efe | ||
|
|
d1096dc31a | ||
|
|
21df094bda | ||
|
|
1276b3e897 | ||
|
|
ad112eae2e | ||
|
|
da750df16b | ||
|
|
767e4cbc41 | ||
|
|
3067fa98f4 | ||
|
|
2309b0dfe2 | ||
|
|
1f5462c80b | ||
|
|
5a983c4a0a | ||
|
|
5662d5932f | ||
|
|
367fe8f74c | ||
|
|
566f7525ee | ||
|
|
fafb69544f | ||
|
|
b7d4602cb8 | ||
|
|
d56861d4ba | ||
|
|
3146754779 | ||
|
|
57695b44b6 | ||
|
|
be52308c99 | ||
|
|
cd7807a484 | ||
|
|
0521060410 | ||
|
|
b9cf522685 | ||
|
|
06e50d57db | ||
|
|
c88fc889e4 | ||
|
|
c15a561b63 | ||
|
|
1819c3bf15 | ||
|
|
f4d7876838 | ||
|
|
d87127a309 | ||
|
|
9df75cc959 | ||
|
|
f3e0b9b56d | ||
|
|
0f03ddcf0f | ||
|
|
14e5691190 | ||
|
|
64b93c9116 | ||
|
|
4038318adc | ||
|
|
b1c6ec3c09 | ||
|
|
f6db6ce861 | ||
|
|
1bc0f42f71 | ||
|
|
01aeeb35e2 | ||
|
|
a6342c9283 | ||
|
|
98b852aabe | ||
|
|
c85203bccd | ||
|
|
ad2a5fd5fc | ||
|
|
04b4f56a66 | ||
|
|
40fbba7df1 | ||
|
|
a23276d3ff | ||
|
|
e556901b9d | ||
|
|
1ae21e6fd4 | ||
|
|
e2d5c2dee0 | ||
|
|
075066dd7c | ||
|
|
3f0aea131e | ||
|
|
92539ee05d | ||
|
|
705b54fcd3 | ||
|
|
1a81227d61 | ||
|
|
9c0efe6f5c | ||
|
|
f9621af7e4 | ||
|
|
6052a5b4d7 | ||
|
|
42437f77d1 | ||
|
|
523362765f | ||
|
|
1459dd4b8e | ||
|
|
1b86a6fc89 | ||
|
|
5cf4fc52da | ||
|
|
cd049c9b4b | ||
|
|
86ebc23de7 | ||
|
|
a236cc4f28 | ||
|
|
6eac58d4b0 | ||
|
|
2994c1c5a7 | ||
|
|
fa4a0757b8 | ||
|
|
49e120c28e | ||
|
|
6479cbaf91 | ||
|
|
36995fd4ed | ||
|
|
fe450eb69d | ||
|
|
edda743fa2 | ||
|
|
57281df53c | ||
|
|
1f767ebd2e | ||
|
|
b7dc384547 | ||
|
|
c588e963de | ||
|
|
d13b91301c | ||
|
|
948dae5e28 | ||
|
|
c659a875fc | ||
|
|
f0d3f36f6d | ||
|
|
940a50e851 | ||
|
|
06542b4bf1 | ||
|
|
af826d500f | ||
|
|
cbe2fb4a4c | ||
|
|
032576c728 | ||
|
|
3099909113 | ||
|
|
097c7912b0 | ||
|
|
493c684eb7 | ||
|
|
a1f98ad78e | ||
|
|
e66de8c4ce | ||
|
|
aafe669739 | ||
|
|
e5e613acee | ||
|
|
ed73485d92 | ||
|
|
50916f2c93 | ||
|
|
c1410d1699 | ||
|
|
c5b72eee16 | ||
|
|
8cfb717dc1 | ||
|
|
639aa12931 | ||
|
|
123b8a66ee | ||
|
|
5d4e591f11 | ||
|
|
9f494fa884 | ||
|
|
9998e7620d | ||
|
|
b6b06123db | ||
|
|
9192b26fab | ||
|
|
65fff11e58 | ||
|
|
00aaae86d8 | ||
|
|
d6bcb96105 | ||
|
|
045a06350b | ||
|
|
fbc9a3b6e1 | ||
|
|
9faa69422d | ||
|
|
8b82d59bd7 | ||
|
|
799869bcef | ||
|
|
8b49f708f3 | ||
|
|
e0200e7fa0 | ||
|
|
d8357ceb94 | ||
|
|
a89a5cfa2e | ||
|
|
4f6e833d34 | ||
|
|
6431be7771 | ||
|
|
766a15b25a | ||
|
|
4e146fb152 | ||
|
|
b8a5f3bab8 | ||
|
|
3051b1ca34 | ||
|
|
7471b76794 | ||
|
|
5af8178802 | ||
|
|
363294c310 | ||
|
|
80dddc35a4 | ||
|
|
dee423519e | ||
|
|
3af366788f | ||
|
|
cf16769a36 | ||
|
|
ee85d13d59 | ||
|
|
f8fa35cf9d | ||
|
|
14a3537ce9 | ||
|
|
0995e008fe | ||
|
|
125341bdaa | ||
|
|
aca58064c3 | ||
|
|
10f49e6146 | ||
|
|
c29860d418 | ||
|
|
947e94f353 | ||
|
|
8adde6bf87 | ||
|
|
d4dcc7a399 | ||
|
|
78ee280e73 | ||
|
|
74d9a750ca | ||
|
|
c365c53332 | ||
|
|
9f092f3928 | ||
|
|
252c734310 | ||
|
|
30de25166d | ||
|
|
a1aaa0a2d1 | ||
|
|
2a27344e9c | ||
|
|
3f769bf0e0 | ||
|
|
9b914fbc0b | ||
|
|
110cb54a8a |
1
.github/CODEOWNERS
vendored
@@ -9,4 +9,5 @@
|
||||
# Unless a later match takes precedence, these owners will be requested for
|
||||
# review when someone opens a pull request.
|
||||
|
||||
/.github/settings.yml @k8sgpt-ai/maintainers
|
||||
* @k8sgpt-ai/maintainers @k8sgpt-ai/k8sgpt-maintainers @k8sgpt-ai/k8sgpt-approvers
|
||||
|
||||
35
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,35 +0,0 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest an idea for this project
|
||||
title: 'feature: '
|
||||
labels: 'type:question'
|
||||
assignees: '@k8sgpt-ai/maintainers'
|
||||
---
|
||||
|
||||
<!--
|
||||
Thank you for initiating this feature request 🤗
|
||||
|
||||
To ensure conciseness, kindly try to adhere to the following format.
|
||||
-->
|
||||
|
||||
Checklist:
|
||||
* [ ] I've searched for similar issues and couldn't find anything matching
|
||||
* [ ] I've discussed this feature in the #k8sgpt slack channel
|
||||
|
||||
## Is this feature request related to a problem?
|
||||
* [ ] Yes
|
||||
* [ ] No
|
||||
|
||||
<!-- If yes, please provide a clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
|
||||
|
||||
## Describe the solution you'd like
|
||||
<!-- A clear and concise description of what you want to happen. -->
|
||||
|
||||
## Benefits for the project and its users
|
||||
<!-- Describe the benefits this feature will bring to the project and its users. -->
|
||||
|
||||
## Potential drawbacks
|
||||
<!-- Describe any potential drawbacks this feature might bring to the project and its users. -->
|
||||
|
||||
## Additional context
|
||||
<!-- Add any other context about your feature request here. If applicable, add drawings to help explain. -->
|
||||
50
.github/ISSUE_TEMPLATE/question.md
vendored
@@ -1,50 +0,0 @@
|
||||
---
|
||||
name: Question / Bug Report
|
||||
about: Create a report to help us improve
|
||||
title: 'question: '
|
||||
labels: 'type:question'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
<!--
|
||||
Thank you for initiating this issue request 🤗
|
||||
|
||||
To ensure conciseness, kindly try to adhere to the following format.
|
||||
-->
|
||||
|
||||
Checklist:
|
||||
|
||||
* [ ] I've searched for similar issues and couldn't find anything matching
|
||||
* [ ] I've included steps to reproduce the bug.
|
||||
* [ ] I've included the version of Kubernetes and k8sgpt.
|
||||
|
||||
### Subject of the issue
|
||||
|
||||
<!-- Describe your issue here. -->
|
||||
|
||||
### Your environment
|
||||
|
||||
<!-- Describe your environment here. -->
|
||||
|
||||
* Version of Kubernetes
|
||||
<!-- kubectl version -->
|
||||
* Host OS and its version / If windows, is it WSL?
|
||||
* Version of k8sgpt
|
||||
<!-- k8sgpt version -->
|
||||
|
||||
### Steps to reproduce
|
||||
|
||||
<!-- Tell us how to reproduce this issue. -->
|
||||
|
||||
* Step 1
|
||||
* Step 2
|
||||
|
||||
### Expected behaviour
|
||||
<!-- Tell us what should happen -->
|
||||
|
||||
### Actual behaviour
|
||||
|
||||
<!-- Tell us what happens instead -->
|
||||
|
||||
### Additional context / screenshots
|
||||
<!-- Add any other context about the problem here. If applicable, add screenshots to help explain. -->
|
||||
47
.github/settings.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
repository:
|
||||
name: "k8sgpt"
|
||||
description: "Giving Kubernetes SRE superpowers to everyone"
|
||||
homepage_url: "https://k8sgpt.ai"
|
||||
topics: kubernetes, devops, tooling, openai, sre
|
||||
|
||||
default_branch: main
|
||||
allow_squash_merge: true
|
||||
allow_merge_commit: true
|
||||
allow_rebase_merge: true
|
||||
|
||||
has_wiki: false
|
||||
|
||||
teams:
|
||||
- name: "maintainers"
|
||||
permission: "admin"
|
||||
- name: "k8sgpt-maintainers"
|
||||
permission: "maintain"
|
||||
- name: "k8sgpt-approvers"
|
||||
permission: "push"
|
||||
- name: "contributors"
|
||||
permission: "push"
|
||||
|
||||
branches:
|
||||
- name: main
|
||||
protection:
|
||||
required_pull_request_reviews:
|
||||
required_approving_review_count: 1
|
||||
dismiss_stale_reviews: true
|
||||
require_code_owner_reviews: true
|
||||
dismissal_restrictions: {}
|
||||
code_owner_approval: true
|
||||
required_conversation_resolution: true
|
||||
|
||||
required_status_checks:
|
||||
strict: true
|
||||
contexts:
|
||||
- "DCO"
|
||||
|
||||
enforce_admins: true
|
||||
|
||||
required_linear_history: true
|
||||
|
||||
restrictions:
|
||||
users: []
|
||||
apps: []
|
||||
teams: []
|
||||
18
.github/workflows/build_container.yaml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
|
||||
|
||||
- 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@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@4b4e9c3e2d4531116a6f8ba8e71fc6e2cb6e6c8c # v2
|
||||
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3
|
||||
|
||||
- name: Build Docker Image
|
||||
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 # v4
|
||||
uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5
|
||||
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@0b7f8abb1508181956e8e162db84b466c27e18ce # v3
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # 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@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2
|
||||
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3
|
||||
with:
|
||||
registry: "ghcr.io"
|
||||
username: ${{ github.actor }}
|
||||
@@ -126,10 +126,10 @@ jobs:
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@4b4e9c3e2d4531116a6f8ba8e71fc6e2cb6e6c8c # v2
|
||||
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3
|
||||
|
||||
- name: Build Docker Image
|
||||
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 # v4
|
||||
uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5
|
||||
with:
|
||||
context: .
|
||||
file: ./container/Dockerfile
|
||||
|
||||
18
.github/workflows/golangci_lint.yaml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
name: Run golangci-lint
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
golangci-lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
|
||||
|
||||
- name: golangci-lint
|
||||
uses: reviewdog/action-golangci-lint@24d4af2fc93f5b2b296229e8b0c0f658d25707af # v2
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
reporter: github-pr-check
|
||||
26
.github/workflows/release.yaml
vendored
@@ -23,9 +23,9 @@ jobs:
|
||||
# Release-please creates a PR that tracks all changes
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3
|
||||
|
||||
- uses: google-github-actions/release-please-action@c078ea33917ab8cfa5300e48f4b7e6b16606aede # v3
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
|
||||
|
||||
- uses: google-github-actions/release-please-action@db8f2c60ee802b3748b512940dde88eabd7b7e01 # v3.7.13
|
||||
id: release
|
||||
with:
|
||||
command: manifest
|
||||
@@ -41,17 +41,17 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4
|
||||
with:
|
||||
go-version: '1.20'
|
||||
- name: Download Syft
|
||||
uses: anchore/sbom-action/download-syft@422cb34a0f8b599678c41b21163ea6088edb2624 # v0.14.1
|
||||
uses: anchore/sbom-action/download-syft@fd74a6fb98a204a1ad35bbfae0122c1a302ff88b # v0.15.0
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@f82d6c1c344bcacabba2c841718984797f664a6b # v4
|
||||
uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5
|
||||
with:
|
||||
# either 'goreleaser' (default) or 'goreleaser-pro'
|
||||
distribution: goreleaser
|
||||
@@ -74,23 +74,23 @@ jobs:
|
||||
IMAGE_NAME: k8sgpt
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@4b4e9c3e2d4531116a6f8ba8e71fc6e2cb6e6c8c # v2
|
||||
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2
|
||||
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3
|
||||
with:
|
||||
registry: "ghcr.io"
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build Docker Image
|
||||
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 # v4
|
||||
uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5
|
||||
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@422cb34a0f8b599678c41b21163ea6088edb2624 # v0.14.1
|
||||
uses: anchore/sbom-action@fd74a6fb98a204a1ad35bbfae0122c1a302ff88b # v0.15.0
|
||||
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
|
||||
|
||||
2
.github/workflows/semantic_pr.yaml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
pull-requests: read # Needed for reading prs
|
||||
steps:
|
||||
- name: Validate Pull Request
|
||||
uses: amannn/action-semantic-pull-request@c3cd5d1ea3580753008872425915e343e351ab54 # v5.2.0
|
||||
uses: amannn/action-semantic-pull-request@e9fabac35e210fea40ca5b14c0da95a099eff26f # v5.4.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
||||
12
.github/workflows/test.yaml
vendored
@@ -16,12 +16,16 @@ jobs:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Test
|
||||
run: go test -v ./...
|
||||
- name: Unit Test
|
||||
run: make test
|
||||
|
||||
# - name: Fmt Test
|
||||
# run: fmtFiles=$(make fmt); if [ "$fmtFiles" != "" ];then exit 1; fi
|
||||
|
||||
|
||||
6
.gitignore
vendored
@@ -1,5 +1,9 @@
|
||||
.idea
|
||||
__debug*
|
||||
.DS_Store
|
||||
k8sgpt*
|
||||
!charts/k8sgpt
|
||||
*.vscode
|
||||
dist/
|
||||
|
||||
bin/
|
||||
bin/
|
||||
|
||||
@@ -15,6 +15,8 @@ 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 }}'
|
||||
|
||||
@@ -1 +1 @@
|
||||
{".":"0.2.7"}
|
||||
{".":"0.3.23"}
|
||||
647
CHANGELOG.md
@@ -1,5 +1,652 @@
|
||||
# Changelog
|
||||
|
||||
## [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)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* don't ask for password if backend is localai ([74d9a75](https://github.com/k8sgpt-ai/k8sgpt/commit/74d9a750ca01361eb81fdcc91eb5886ecff1d17c))
|
||||
* introduce linter to run on PR builds ([#333](https://github.com/k8sgpt-ai/k8sgpt/issues/333)) ([252c734](https://github.com/k8sgpt-ai/k8sgpt/commit/252c7343106bf64c86861a9452e8618efc72881c)), closes [#330](https://github.com/k8sgpt-ai/k8sgpt/issues/330)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove dead code ([c29860d](https://github.com/k8sgpt-ai/k8sgpt/commit/c29860d418faa316bc167721e443f7b64eafd970))
|
||||
* report failure if network policy doesn't match any pods ([8adde6b](https://github.com/k8sgpt-ai/k8sgpt/commit/8adde6bf873b46f365146bc14fc4c8f46d82f8dc))
|
||||
* take `KUBECONFIG` env variable into consideration ([#340](https://github.com/k8sgpt-ai/k8sgpt/issues/340)) ([ee85d13](https://github.com/k8sgpt-ai/k8sgpt/commit/ee85d13d59e045519b087adaf55520acc2c205db)), closes [#331](https://github.com/k8sgpt-ai/k8sgpt/issues/331)
|
||||
* use a cache file name with a fixed size. ([#350](https://github.com/k8sgpt-ai/k8sgpt/issues/350)) ([dee4235](https://github.com/k8sgpt-ai/k8sgpt/commit/dee423519eb35f11c3e3a6dd64981e781899fe22))
|
||||
* use correct result slice for cronjob analyzer ([947e94f](https://github.com/k8sgpt-ai/k8sgpt/commit/947e94f35379712a2fb1e2a2c90636606e0e44b6))
|
||||
|
||||
|
||||
### Docs
|
||||
|
||||
* fix README ([#345](https://github.com/k8sgpt-ai/k8sgpt/issues/345)) ([f8fa35c](https://github.com/k8sgpt-ai/k8sgpt/commit/f8fa35cf9d591691679d6881fcc203e3411d99aa))
|
||||
|
||||
|
||||
### Other
|
||||
|
||||
* add settings ([#351](https://github.com/k8sgpt-ai/k8sgpt/issues/351)) ([3af3667](https://github.com/k8sgpt-ai/k8sgpt/commit/3af366788fb47ff87be0142446c027f5a90491e7))
|
||||
* **deps:** pin dependencies ([#336](https://github.com/k8sgpt-ai/k8sgpt/issues/336)) ([125341b](https://github.com/k8sgpt-ai/k8sgpt/commit/125341bdaacbc8bedbb333e498dabfb5c72a24c0))
|
||||
* logo update ([#339](https://github.com/k8sgpt-ai/k8sgpt/issues/339)) ([d4dcc7a](https://github.com/k8sgpt-ai/k8sgpt/commit/d4dcc7a3991a861923c8115c0c82759b9e83bcfa))
|
||||
* update Apache2 license ([#342](https://github.com/k8sgpt-ai/k8sgpt/issues/342)) ([aca5806](https://github.com/k8sgpt-ai/k8sgpt/commit/aca58064c36b3bc13699e055a7cca8a493320078))
|
||||
* update README.md ([#346](https://github.com/k8sgpt-ai/k8sgpt/issues/346)) ([14a3537](https://github.com/k8sgpt-ai/k8sgpt/commit/14a3537ce9bc9d581b78329be899a66bc14db648))
|
||||
* updated banner ([#343](https://github.com/k8sgpt-ai/k8sgpt/issues/343)) ([0995e00](https://github.com/k8sgpt-ai/k8sgpt/commit/0995e008fe64f5978c3a0cc9fb4c525470f00dfa))
|
||||
|
||||
## [0.2.7](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.2.6...v0.2.7) (2023-04-25)
|
||||
|
||||
|
||||
|
||||
@@ -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://slack.cloud-native.io/channels/k8sgpt)
|
||||
* Join our [#k8sgpt slack channel](https://join.slack.com/t/k8sgpt/shared_invite/zt-1rwe5fpzq-VNtJK8DmYbbm~iWL1H34nw)
|
||||
* 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 follow the [pull request template](.github/pull_request_template.md) guidelines.
|
||||
- Create a pull request back to the upstream repository and follow the [pull request template](.github/pull_request_template.md) guidelines.
|
||||
- Wait for a review and address any comments
|
||||
|
||||
**Opening PRs**
|
||||
|
||||
2
LICENSE
@@ -187,7 +187,7 @@
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
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.
|
||||
|
||||
42
Makefile
@@ -10,6 +10,8 @@ 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
|
||||
|
||||
@@ -52,7 +54,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' $(BUILDFILE)
|
||||
@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)
|
||||
|
||||
## tidy: tidy go.mod
|
||||
.PHONY: tidy
|
||||
@@ -61,17 +63,21 @@ tidy:
|
||||
|
||||
## deploy: Deploy k8sgpt
|
||||
.PHONY: deploy
|
||||
deploy:
|
||||
deploy: helm
|
||||
@echo "===========> Deploying k8sgpt"
|
||||
@$(call funcsecret)
|
||||
$(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
|
||||
|
||||
## undeploy: Undeploy k8sgpt
|
||||
.PHONY: undeploy
|
||||
undeploy:
|
||||
undeploy: helm
|
||||
@echo "===========> Undeploying k8sgpt"
|
||||
kubectl delete secret ai-backend-secret --namespace=k8sgpt
|
||||
kubectl delete -f container/manifests
|
||||
kubectl delete ns k8sgpt
|
||||
$(HELM) uninstall k8sgpt -n k8sgpt
|
||||
|
||||
## docker-build: Build docker image
|
||||
.PHONY: docker-build
|
||||
@@ -79,7 +85,6 @@ 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:
|
||||
@@ -137,11 +142,16 @@ 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..."
|
||||
|
||||
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
|
||||
# =====
|
||||
# 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)
|
||||
501
README.md
@@ -7,17 +7,23 @@
|
||||

|
||||

|
||||

|
||||
[](https://bestpractices.coreinfrastructure.org/projects/7272)
|
||||
[](https://docs.k8sgpt.ai/)
|
||||
|
||||
`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 gives Kubernetes Superpowers to everyone | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||
|
||||
# Installation
|
||||
<img src="images/demo4.gif" width=650px; />
|
||||
|
||||
# CLI Installation
|
||||
|
||||
|
||||
## Linux/Mac via brew
|
||||
### Linux/Mac via brew
|
||||
|
||||
```
|
||||
brew tap k8sgpt-ai/k8sgpt
|
||||
@@ -30,7 +36,7 @@ brew install k8sgpt
|
||||
**32 bit:**
|
||||
<!---x-release-please-start-version-->
|
||||
```
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.2.7/k8sgpt_386.rpm
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.23/k8sgpt_386.rpm
|
||||
sudo rpm -ivh k8sgpt_386.rpm
|
||||
```
|
||||
<!---x-release-please-end-->
|
||||
@@ -39,7 +45,7 @@ brew install k8sgpt
|
||||
|
||||
<!---x-release-please-start-version-->
|
||||
```
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.2.7/k8sgpt_amd64.rpm
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.23/k8sgpt_amd64.rpm
|
||||
sudo rpm -ivh -i k8sgpt_amd64.rpm
|
||||
```
|
||||
<!---x-release-please-end-->
|
||||
@@ -51,7 +57,7 @@ brew install k8sgpt
|
||||
**32 bit:**
|
||||
<!---x-release-please-start-version-->
|
||||
```
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.2.7/k8sgpt_386.deb
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.23/k8sgpt_386.deb
|
||||
sudo dpkg -i k8sgpt_386.deb
|
||||
```
|
||||
<!---x-release-please-end-->
|
||||
@@ -59,7 +65,7 @@ brew install k8sgpt
|
||||
|
||||
<!---x-release-please-start-version-->
|
||||
```
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.2.7/k8sgpt_amd64.deb
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.23/k8sgpt_amd64.deb
|
||||
sudo dpkg -i k8sgpt_amd64.deb
|
||||
```
|
||||
<!---x-release-please-end-->
|
||||
@@ -72,14 +78,14 @@ brew install k8sgpt
|
||||
**32 bit:**
|
||||
<!---x-release-please-start-version-->
|
||||
```
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.2.7/k8sgpt_386.apk
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.23/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.2.7/k8sgpt_amd64.apk
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.23/k8sgpt_amd64.apk
|
||||
apk add k8sgpt_amd64.apk
|
||||
```
|
||||
<!---x-release-please-end-->x
|
||||
@@ -102,30 +108,29 @@ 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
|
||||
|
||||
## Verify installation
|
||||
To install within a Kubernetes cluster please use our `k8sgpt-operator` with installation instructions available [here](https://github.com/k8sgpt-ai/k8sgpt-operator)
|
||||
|
||||
* Run `k8sgpt version`
|
||||
_This mode of operation is ideal for continuous monitoring of your cluster and can integrate with your existing monitoring such as Prometheus and Alertmanager._
|
||||
|
||||
<hr>
|
||||
|
||||
## Quick Start
|
||||
|
||||
* 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` to set it in k8sgpt.
|
||||
* Run `k8sgpt auth add` 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.
|
||||
|
||||
<img src="images/demo4.gif" width=650px; />
|
||||
* You also run `k8sgpt analyze --with-doc` (with or without the explain flag) to get the official documentation from kubernetes.
|
||||
|
||||
## Analyzers
|
||||
|
||||
@@ -146,77 +151,27 @@ 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
|
||||
|
||||
## 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`
|
||||
## Examples
|
||||
|
||||
_Run a scan with the default analyzers_
|
||||
|
||||
```
|
||||
k8sgpt generate
|
||||
k8sgpt auth
|
||||
k8sgpt auth add
|
||||
k8sgpt analyze --explain
|
||||
k8sgpt analyze --explain --with-doc
|
||||
```
|
||||
|
||||
_Filter on resource_
|
||||
@@ -242,41 +197,60 @@ _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>
|
||||
|
||||
1. Error reported during analysis:
|
||||
```bash
|
||||
Error: HorizontalPodAutoscaler uses StatefulSet/fake-deployment as ScaleTargetRef which does not exist.
|
||||
_List filters_
|
||||
|
||||
```
|
||||
k8sgpt filters list
|
||||
```
|
||||
|
||||
2. Payload sent to the AI backend:
|
||||
```bash
|
||||
Error: HorizontalPodAutoscaler uses StatefulSet/tGLcCRcHa1Ce5Rs as ScaleTargetRef which does not exist.
|
||||
_Add default filters_
|
||||
|
||||
```
|
||||
k8sgpt filters add [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 :
|
||||
|
||||
- Simple filter : `k8sgpt filters add Service`
|
||||
- Multiple filters : `k8sgpt filters add Ingress,Pod`
|
||||
|
||||
_Remove default filters_
|
||||
|
||||
```
|
||||
k8sgpt filters remove [filter(s)]
|
||||
```
|
||||
|
||||
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.
|
||||
```
|
||||
### Examples :
|
||||
|
||||
**Anonymization does not currently apply to events.**
|
||||
- Simple filter : `k8sgpt filters remove Service`
|
||||
- Multiple filters : `k8sgpt filters remove Ingress,Pod`
|
||||
|
||||
</details>
|
||||
|
||||
### Additional commands
|
||||
|
||||
<details>
|
||||
|
||||
_Manage integrations_
|
||||
<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..
|
||||
```
|
||||
|
||||
_List integrations_
|
||||
|
||||
@@ -315,23 +289,356 @@ curl -X GET "http://localhost:8080/analyze?namespace=k8sgpt&explain=false"
|
||||
```
|
||||
</details>
|
||||
|
||||
## Configuration
|
||||
|
||||
## Key Features
|
||||
|
||||
<details>
|
||||
<summary> LocalAI provider </summary>
|
||||
|
||||
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.
|
||||
|
||||
|
||||
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 add` with the `localai` backend:
|
||||
|
||||
```
|
||||
k8sgpt auth add --backend localai --model <model_name> --baseurl http://localhost:8080/v1 --temperature 0.7
|
||||
```
|
||||
|
||||
Now you can analyze with the `localai` backend:
|
||||
|
||||
```
|
||||
k8sgpt analyze --explain --backend localai
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary> AzureOpenAI provider </summary>
|
||||
|
||||
<em>Prerequisites:</em> an Azure OpenAI deployment is needed, please visit MS official [documentation](https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource) to create your own.
|
||||
|
||||
To authenticate with k8sgpt, you will need the Azure OpenAI endpoint of your tenant `"https://your Azure OpenAI Endpoint"`, the api key to access your deployment, the deployment name of your model and the model name itself.
|
||||
|
||||
|
||||
To run k8sgpt, run `k8sgpt auth` with the `azureopenai` backend:
|
||||
```
|
||||
k8sgpt auth add --backend azureopenai --baseurl https://<your Azure OpenAI endpoint> --engine <deployment_name> --model <model_name>
|
||||
```
|
||||
Lastly, enter your Azure API key, after the prompt.
|
||||
|
||||
Now you are ready to analyze with the azure openai backend:
|
||||
```
|
||||
k8sgpt analyze --explain --backend azureopenai
|
||||
```
|
||||
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Cohere provider</summary>
|
||||
|
||||
<em>Prerequisites:</em> a Cohere API key is needed, please visit the [Cohere dashboard](https://dashboard.cohere.ai/api-keys) to create one.
|
||||
|
||||
To run k8sgpt, run `k8sgpt auth` with the `cohere` backend:
|
||||
|
||||
```
|
||||
k8sgpt auth add --backend cohere --model command-nightly
|
||||
```
|
||||
|
||||
Lastly, enter your Cohere API key, after the prompt.
|
||||
|
||||
Now you are ready to analyze with the Cohere backend:
|
||||
|
||||
```
|
||||
k8sgpt analyze --explain --backend cohere
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Amazon Bedrock provider</summary>
|
||||
|
||||
|
||||
<em>Prerequisites</em>
|
||||
Bedrock API access is needed.
|
||||
|
||||
<img src="images/bedrock.png" width="500px;" />
|
||||
|
||||
As illustrated below, you will need to enable this in the [AWS Console](https://eu-central-1.console.aws.amazon.com/bedrock/home?region=eu-central-1#/modelaccess)
|
||||
|
||||
In addition to this you will need to set the follow local environmental variables:
|
||||
|
||||
|
||||
```
|
||||
- AWS_ACCESS_KEY
|
||||
- AWS_SECRET_ACCESS_KEY
|
||||
- AWS_DEFAULT_REGION
|
||||
```
|
||||
|
||||
|
||||
```
|
||||
k8sgpt auth add --backend amazonbedrock --model anthropic.claude-v2
|
||||
```
|
||||
|
||||
#### Usage
|
||||
|
||||
```
|
||||
k8sgpt analyze -e -b amazonbedrock
|
||||
|
||||
0 argocd/argocd-application-controller(argocd-application-controller)
|
||||
- Error: StatefulSet uses the service argocd/argocd-application-controller which does not exist.
|
||||
|
||||
You're right, I don't have enough context to determine if a StatefulSet is correctly configured to use a non-existent service. A StatefulSet manages Pods with persistent storage, and the Pods are created from the same spec. The service name referenced in the StatefulSet configuration would need to match an existing Kubernetes service for the Pods to connect to. Without more details on the specific StatefulSet and environment, I can't confirm whether the configuration is valid or not.
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Amazon SageMaker Provider</summary>
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
1. **AWS CLI Configuration**: Make sure you have the AWS Command Line Interface (CLI) configured on your machine. If you haven't already configured the AWS CLI, you can follow the official AWS documentation for instructions on how to do it: [AWS CLI Configuration Guide](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html).
|
||||
|
||||
2. **SageMaker Instance**: You need to have an Amazon SageMaker instance set up. If you don't have one already, you can follow the step-by-step instructions provided in this repository for creating a SageMaker instance: [llm-sagemaker-jumpstart-cdk](https://github.com/zaremb/llm-sagemaker-jumpstart-cdk).
|
||||
|
||||
#### Backend Configuration
|
||||
|
||||
To add amazonsagemaker backend two parameters are required:
|
||||
|
||||
* `--endpointname` Amazon SageMaker endpoint name.
|
||||
* `--providerRegion` AWS region where SageMaker instance is created. `k8sgpt` uses this region to connect to SageMaker (not the one defined with AWS CLI or environment variables )
|
||||
|
||||
To add amazonsagemaker as a backend run:
|
||||
|
||||
```bash
|
||||
k8sgpt auth add --backend amazonsagemaker --providerRegion eu-west-1 --endpointname endpoint-xxxxxxxxxx
|
||||
```
|
||||
|
||||
#### Optional params
|
||||
|
||||
Optionally, when adding the backend and later by changing the configuration file, you can set the following parameters:
|
||||
|
||||
`-l, --maxtokens int` Specify a maximum output length. Adjust (1-...) to control text length. Higher values produce longer output, lower values limit length (default 2048)
|
||||
|
||||
`-t, --temperature float32` The sampling temperature, value ranges between 0 ( output be more deterministic) and 1 (more random) (default 0.7)
|
||||
|
||||
`-c, --topp float32` Probability Cutoff: Set a threshold (0.0-1.0) to limit word choices. Higher values add randomness, lower values increase predictability. (default 0.5)
|
||||
|
||||
To make amazonsagemaker as a default backend run:
|
||||
|
||||
```bash
|
||||
k8sgpt auth default -p amazonsagemaker
|
||||
```
|
||||
|
||||
#### AmazonSageMaker Usage
|
||||
|
||||
```bash
|
||||
./k8sgpt analyze -e -b amazonsagemaker
|
||||
100% |███████████████████████████████████████████████████████████████████████████████████████████████████████████████████| (1/1, 14 it/min)
|
||||
AI Provider: amazonsagemaker
|
||||
|
||||
0 default/nginx(nginx)
|
||||
- Error: Back-off pulling image "nginxx"
|
||||
Error: Back-off pulling image "nginxx"
|
||||
|
||||
Solution:
|
||||
|
||||
1. Check if the image exists in the registry by running `docker image ls nginxx`.
|
||||
2. If the image is not found, try pulling it by running `docker pull nginxx`.
|
||||
3. If the image is still not available, check if there are any network issues by running `docker network inspect` and `docker network list`.
|
||||
4. If the issue persists, try restarting the Docker daemon by running `sudo service docker restart`.
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Setting a new default AI provider</summary>
|
||||
|
||||
There may be scenarios where you wish to have K8sGPT plugged into several default AI providers. In this case you may wish to use one as a new default, other than OpenAI which is the project default.
|
||||
|
||||
_To view available providers_
|
||||
|
||||
```
|
||||
k8sgpt auth list
|
||||
Default:
|
||||
> openai
|
||||
Active:
|
||||
> openai
|
||||
> azureopenai
|
||||
Unused:
|
||||
> localai
|
||||
> noopai
|
||||
> amazonbedrock
|
||||
> cohere
|
||||
|
||||
```
|
||||
|
||||
|
||||
_To set a new default provider_
|
||||
|
||||
```
|
||||
k8sgpt auth default -p azureopenai
|
||||
Default provider set to azureopenai
|
||||
```
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<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>
|
||||
|
||||
<details>
|
||||
<summary> Configuration management</summary>
|
||||
|
||||
`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-1rwe5fpzq-VNtJK8DmYbbm~iWL1H34nw)
|
||||
Find us on [Slack](https://join.slack.com/t/k8sgpt/shared_invite/zt-276pa9uyq-pxAUr4TCVHubFxEvLZuT1Q)
|
||||
|
||||
<a href="https://github.com/k8sgpt-ai/k8sgpt/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=k8sgpt-ai/k8sgpt" />
|
||||
|
||||
@@ -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 vulnverability 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 vulnerability please feel free to disclose it responsibly to contact@k8sgpt.ai or to one of our maintainers in our Slack community.
|
||||
|
||||
6
charts/k8sgpt/Chart.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
apiVersion: v2
|
||||
appVersion: v0.3.0 #x-release-please-version
|
||||
description: A Helm chart for K8SGPT
|
||||
name: k8sgpt
|
||||
type: application
|
||||
version: 1.0.0
|
||||
44
charts/k8sgpt/templates/_helpers.tpl
Normal file
@@ -0,0 +1,44 @@
|
||||
{{/*
|
||||
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 }}
|
||||
57
charts/k8sgpt/templates/deployment.yaml
Normal file
@@ -0,0 +1,57 @@
|
||||
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
|
||||
@@ -1,7 +1,10 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: k8sgpt-cluster-role-all
|
||||
name: {{ template "k8sgpt.fullname" . }}
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
labels:
|
||||
{{- include "k8sgpt.labels" . | nindent 4 }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- '*'
|
||||
15
charts/k8sgpt/templates/rolebinding.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
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
|
||||
7
charts/k8sgpt/templates/sa.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ template "k8sgpt.fullname" . }}
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
labels:
|
||||
{{- include "k8sgpt.labels" . | nindent 4 }}
|
||||
10
charts/k8sgpt/templates/secret.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
{{- 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}}
|
||||
22
charts/k8sgpt/templates/service.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
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 }}
|
||||
21
charts/k8sgpt/templates/serviceMonitor.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
{{- 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 }}
|
||||
27
charts/k8sgpt/values.yaml
Normal file
@@ -0,0 +1,27 @@
|
||||
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: {}
|
||||
@@ -32,6 +32,7 @@ var (
|
||||
namespace string
|
||||
anonymize bool
|
||||
maxConcurrency int
|
||||
withDoc bool
|
||||
)
|
||||
|
||||
// AnalyzeCmd represents the problems command
|
||||
@@ -45,18 +46,13 @@ var AnalyzeCmd = &cobra.Command{
|
||||
|
||||
// AnalysisResult configuration
|
||||
config, err := analysis.NewAnalysis(backend,
|
||||
language, filters, namespace, nocache, explain, maxConcurrency)
|
||||
language, filters, namespace, nocache, explain, maxConcurrency, withDoc)
|
||||
if err != nil {
|
||||
color.Red("Error: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
analysisErrors := config.RunAnalysis()
|
||||
if len(analysisErrors) != 0 {
|
||||
for _, err := range analysisErrors {
|
||||
color.Red("Error: %s", err)
|
||||
}
|
||||
}
|
||||
config.RunAnalysis()
|
||||
|
||||
if explain {
|
||||
err := config.GetAIResults(output, anonymize)
|
||||
@@ -96,4 +92,6 @@ 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")
|
||||
}
|
||||
|
||||
163
cmd/auth/add.go
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
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`)")
|
||||
// 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")
|
||||
//add flag for amazonbedrock region name
|
||||
addCmd.Flags().StringVarP(&providerRegion, "providerRegion", "r", "", "Provider Region name")
|
||||
}
|
||||
110
cmd/auth/auth.go
@@ -14,107 +14,47 @@ 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
|
||||
model string
|
||||
backend string
|
||||
password string
|
||||
baseURL string
|
||||
endpointName string
|
||||
model string
|
||||
engine string
|
||||
temperature float32
|
||||
providerRegion string
|
||||
topP float32
|
||||
maxTokens int
|
||||
)
|
||||
|
||||
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) {
|
||||
|
||||
// get ai configuration
|
||||
var configAI ai.AIConfiguration
|
||||
err := viper.UnmarshalKey("ai", &configAI)
|
||||
if err != nil {
|
||||
color.Red("Error: %v", err)
|
||||
os.Exit(1)
|
||||
if len(args) == 0 {
|
||||
_ = cmd.Help()
|
||||
return
|
||||
}
|
||||
|
||||
// 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 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 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`)")
|
||||
// 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)
|
||||
}
|
||||
|
||||
79
cmd/auth/default.go
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
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")
|
||||
}
|
||||
104
cmd/auth/list.go
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
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)
|
||||
}
|
||||
}
|
||||
77
cmd/auth/remove.go
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
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)")
|
||||
}
|
||||
113
cmd/auth/update.go
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
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
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
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(®ion, "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")
|
||||
}
|
||||
36
cmd/cache/cache.go
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
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) {
|
||||
cmd.Help()
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
}
|
||||
67
cmd/cache/list.go
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
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
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
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
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
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)
|
||||
}
|
||||
@@ -45,6 +45,13 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
},
|
||||
|
||||
@@ -18,6 +18,7 @@ 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"
|
||||
@@ -30,27 +31,33 @@ 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.Printf(color.YellowString("Active: \n"))
|
||||
fmt.Print(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 {
|
||||
fmt.Printf("> %s\n", color.GreenString(filter))
|
||||
// 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// display inactive filters
|
||||
if len(inactiveFilters) != 0 {
|
||||
fmt.Printf(color.YellowString("Unused: \n"))
|
||||
fmt.Print(color.YellowString("Unused: \n"))
|
||||
for _, filter := range inactiveFilters {
|
||||
// if the filter is an integration, mark this differently
|
||||
if util.SliceContainsString(integrationFilters, filter) {
|
||||
|
||||
@@ -15,8 +15,14 @@ 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
|
||||
@@ -27,10 +33,17 @@ 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)
|
||||
err := integration.Activate(integrationName, namespace, activeFilters, skipInstall)
|
||||
if err != nil {
|
||||
color.Red("Error: %v", err)
|
||||
return
|
||||
@@ -42,5 +55,6 @@ 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)")
|
||||
|
||||
}
|
||||
|
||||
@@ -25,14 +25,14 @@ var (
|
||||
var IntegrationCmd = &cobra.Command{
|
||||
Use: "integration",
|
||||
Aliases: []string{"integrations"},
|
||||
Short: "Intergrate another tool into K8sGPT",
|
||||
Long: `Intergrate another tool into K8sGPT. For example:
|
||||
Short: "Integrate another tool into K8sGPT",
|
||||
Long: `Integrate 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()
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
37
cmd/root.go
@@ -17,25 +17,27 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/adrg/xdg"
|
||||
"github.com/fatih/color"
|
||||
"github.com/k8sgpt-ai/k8sgpt/cmd/serve"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||
"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"
|
||||
"github.com/k8sgpt-ai/k8sgpt/cmd/serve"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"k8s.io/client-go/util/homedir"
|
||||
)
|
||||
|
||||
var (
|
||||
cfgFile string
|
||||
kubecontext string
|
||||
kubeconfig string
|
||||
version string
|
||||
Version string
|
||||
Commit string
|
||||
Date string
|
||||
)
|
||||
|
||||
// rootCmd represents the base command when called without any subcommands
|
||||
@@ -50,8 +52,10 @@ 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) {
|
||||
version = v
|
||||
func Execute(v string, c string, d string) {
|
||||
Version = v
|
||||
Commit = c
|
||||
Date = d
|
||||
err := rootCmd.Execute()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
@@ -63,19 +67,16 @@ func init() {
|
||||
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
var kubeconfigPath string
|
||||
if home := homedir.HomeDir(); home != "" {
|
||||
kubeconfigPath = filepath.Join(home, ".kube", "config")
|
||||
}
|
||||
rootCmd.AddCommand(auth.AuthCmd)
|
||||
rootCmd.AddCommand(analyze.AnalyzeCmd)
|
||||
rootCmd.AddCommand(filters.FiltersCmd)
|
||||
rootCmd.AddCommand(generate.GenerateCmd)
|
||||
rootCmd.AddCommand(integration.IntegrationCmd)
|
||||
rootCmd.AddCommand(serve.ServeCmd)
|
||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.k8sgpt.yaml)")
|
||||
rootCmd.AddCommand(cache.CacheCmd)
|
||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", fmt.Sprintf("Default config file (%s/k8sgpt/k8sgpt.yaml)", xdg.ConfigHome))
|
||||
rootCmd.PersistentFlags().StringVar(&kubecontext, "kubecontext", "", "Kubernetes context to use. Only required if out-of-cluster.")
|
||||
rootCmd.PersistentFlags().StringVar(&kubeconfig, "kubeconfig", kubeconfigPath, "Path to a kubeconfig. Only required if out-of-cluster.")
|
||||
rootCmd.PersistentFlags().StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
|
||||
}
|
||||
|
||||
// initConfig reads in config file and ENV variables if set.
|
||||
@@ -91,7 +92,7 @@ func initConfig() {
|
||||
viper.SetConfigType("yaml")
|
||||
viper.SetConfigName("k8sgpt")
|
||||
|
||||
viper.SafeWriteConfig()
|
||||
_ = viper.SafeWriteConfig()
|
||||
}
|
||||
|
||||
viper.Set("kubecontext", kubecontext)
|
||||
@@ -102,6 +103,7 @@ 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())
|
||||
}
|
||||
}
|
||||
@@ -120,14 +122,7 @@ 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)
|
||||
}
|
||||
|
||||
@@ -15,18 +15,24 @@ 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
|
||||
backend string
|
||||
token string
|
||||
port string
|
||||
metricsPort string
|
||||
backend string
|
||||
)
|
||||
|
||||
var ServeCmd = &cobra.Command{
|
||||
@@ -43,22 +49,40 @@ 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 != "" || baseURL != ""
|
||||
|
||||
envIsSet := backend != "" || password != "" || model != ""
|
||||
if envIsSet {
|
||||
aiProvider = &ai.AIProvider{
|
||||
Name: backend,
|
||||
Password: password,
|
||||
Model: model,
|
||||
BaseURL: baseURL,
|
||||
Name: backend,
|
||||
Password: password,
|
||||
Model: model,
|
||||
BaseURL: baseURL,
|
||||
Engine: engine,
|
||||
Temperature: temperature(),
|
||||
}
|
||||
|
||||
configAI.Providers = append(configAI.Providers, *aiProvider)
|
||||
@@ -76,10 +100,10 @@ var ServeCmd = &cobra.Command{
|
||||
if aiProvider == nil {
|
||||
for _, provider := range configAI.Providers {
|
||||
if backend == provider.Name {
|
||||
// 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
|
||||
// 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
|
||||
aiProvider = &p
|
||||
break
|
||||
}
|
||||
@@ -91,23 +115,42 @@ var ServeCmd = &cobra.Command{
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
server := k8sgptserver.Config{
|
||||
Backend: aiProvider.Name,
|
||||
Port: port,
|
||||
Token: aiProvider.Password,
|
||||
}
|
||||
|
||||
err = server.Serve()
|
||||
logger, err := zap.NewProduction()
|
||||
if err != nil {
|
||||
color.Red("Error: %v", err)
|
||||
color.Red("failed to create logger: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
// override the default backend if a flag is provided
|
||||
defer logger.Sync()
|
||||
|
||||
server := k8sgptserver.Config{
|
||||
Backend: aiProvider.Name,
|
||||
Port: port,
|
||||
MetricsPort: metricsPort,
|
||||
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 {}
|
||||
},
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@ limitations under the License.
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -23,7 +26,21 @@ 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) {
|
||||
cmd.Printf("k8sgpt version %s\n", version)
|
||||
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)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,12 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM golang:1.20.3-alpine3.16 AS builder
|
||||
FROM golang:1.20.4-alpine3.16 AS builder
|
||||
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
ARG VERSION
|
||||
ARG COMMIT
|
||||
ARG DATE
|
||||
WORKDIR /workspace
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
@@ -20,7 +22,7 @@ RUN go mod download
|
||||
|
||||
COPY ./ ./
|
||||
|
||||
RUN go build -o /workspace/k8sgpt ./
|
||||
RUN go build -o /workspace/k8sgpt -ldflags "-X main.version=${VERSION} -X main.commit=${COMMIT} -X main.date=${DATE}" ./
|
||||
|
||||
FROM gcr.io/distroless/static AS production
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
"showLineNumbers": false,
|
||||
"showMiniMap": false
|
||||
},
|
||||
"content": "",
|
||||
"content": "",
|
||||
"mode": "markdown"
|
||||
},
|
||||
"pluginVersion": "9.4.7",
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
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.7 #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
|
||||
@@ -1,13 +0,0 @@
|
||||
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
|
||||
@@ -1,5 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: k8sgpt
|
||||
namespace: k8sgpt
|
||||
@@ -1,15 +0,0 @@
|
||||
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
|
||||
@@ -1,15 +0,0 @@
|
||||
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
|
||||
218
go.mod
@@ -3,76 +3,122 @@ module github.com/k8sgpt-ai/k8sgpt
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/aquasecurity/trivy-operator v0.13.1
|
||||
github.com/fatih/color v1.15.0
|
||||
github.com/aquasecurity/trivy-operator v0.16.4
|
||||
github.com/fatih/color v1.16.0
|
||||
github.com/magiconair/properties v1.8.7
|
||||
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
|
||||
github.com/mittwald/go-helm-client v0.12.4
|
||||
github.com/sashabaranov/go-openai v1.17.8
|
||||
github.com/schollz/progressbar/v3 v3.14.1
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/viper v1.17.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
golang.org/x/term v0.14.0
|
||||
helm.sh/helm/v3 v3.13.2
|
||||
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
|
||||
|
||||
)
|
||||
|
||||
require github.com/adrg/xdg v0.4.0
|
||||
|
||||
require (
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect
|
||||
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.3.0-20231116211251-9f5041346631.2
|
||||
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.28.1-20231116211251-9f5041346631.4
|
||||
cloud.google.com/go/storage v1.35.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0
|
||||
github.com/aws/aws-sdk-go v1.48.3
|
||||
github.com/cohere-ai/cohere-go v0.2.0
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
sigs.k8s.io/controller-runtime v0.16.3
|
||||
sigs.k8s.io/gateway-api v1.0.0
|
||||
google.golang.org/api v0.151.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.110.8 // indirect
|
||||
cloud.google.com/go/compute v1.23.1 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
cloud.google.com/go/iam v1.1.3 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 // indirect
|
||||
github.com/Microsoft/hcsshim v0.11.0 // indirect
|
||||
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect
|
||||
github.com/cohere-ai/tokenizer v1.1.1 // indirect
|
||||
github.com/dlclark/regexp2 v1.4.0 // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.7.0 // 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/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||
github.com/sagikazarmark/locafero v0.3.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/BurntSushi/toml v1.2.1 // indirect
|
||||
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||
github.com/MakeNowJust/heredoc v1.0.0 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.0 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.1 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 // 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/Masterminds/squirrel v1.5.4 // indirect
|
||||
github.com/aquasecurity/defsec v0.93.1 // indirect
|
||||
github.com/aquasecurity/go-dep-parser v0.0.0-20230830122616-841bc0f812c7 // indirect
|
||||
github.com/aquasecurity/table v1.8.0 // indirect
|
||||
github.com/aquasecurity/tml v0.6.1 // indirect
|
||||
github.com/aquasecurity/trivy v0.40.0 // indirect
|
||||
github.com/aquasecurity/trivy-db v0.0.0-20230411140759-3c2ee2168575 // indirect
|
||||
github.com/aquasecurity/trivy v0.45.1 // indirect
|
||||
github.com/aquasecurity/trivy-db v0.0.0-20230831170347-f732860d4917 // 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.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/containerd/containerd v1.7.6 // 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.6+incompatible // indirect
|
||||
github.com/docker/distribution v2.8.2+incompatible // indirect
|
||||
github.com/docker/docker v24.0.7+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.10.2 // indirect
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/evanphx/json-patch v5.7.0+incompatible // indirect
|
||||
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.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.2.4 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.20.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/go-openapi/swag v0.22.4 // 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.6.9 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/go-containerregistry v0.14.0 // 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/gofuzz v1.2.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/google/uuid v1.4.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
|
||||
@@ -80,22 +126,21 @@ 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.15 // indirect
|
||||
github.com/imdario/mergo v0.3.16 // 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.16.0 // indirect
|
||||
github.com/klauspost/compress v1.17.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.7 // indirect
|
||||
github.com/lib/pq v1.10.9 // 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-20221225060805-c02764233454 // indirect
|
||||
github.com/masahiro331/go-xfs-filesystem v0.0.0-20230608043311-a335f4599b70 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.18 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // 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
|
||||
@@ -103,81 +148,74 @@ 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.0.0-20221205130635-1aeaba878587 // indirect
|
||||
github.com/moby/term v0.5.0 // 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-rc2.0.20221020182949-4df8887994e8 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.7 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // 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.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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_golang v1.17.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/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/rubenv/sql-migrate v1.3.1 // indirect
|
||||
github.com/rubenv/sql-migrate v1.5.2 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/samber/lo v1.37.0 // indirect
|
||||
github.com/samber/lo v1.38.1 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // 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/afero v1.10.0 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/subosito/gotenv v1.4.2 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
github.com/xlab/treeprint v1.1.0 // indirect
|
||||
github.com/xlab/treeprint v1.2.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.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
|
||||
go.uber.org/multierr v1.11.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
|
||||
go.uber.org/zap v1.26.0
|
||||
golang.org/x/crypto v0.14.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
|
||||
golang.org/x/net v0.17.0 // indirect
|
||||
golang.org/x/oauth2 v0.13.0 // indirect
|
||||
golang.org/x/sync v0.5.0 // indirect
|
||||
golang.org/x/sys v0.14.0 // indirect
|
||||
golang.org/x/text v0.13.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
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/grpc v1.59.0
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // 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
|
||||
k8s.io/apiextensions-apiserver v0.28.3 // indirect
|
||||
k8s.io/apiserver v0.28.3 // 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
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // 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
|
||||
sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect
|
||||
sigs.k8s.io/yaml v1.4.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.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
|
||||
replace oras.land/oras-go => oras.land/oras-go v1.2.4
|
||||
|
||||
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 78 KiB |
10
images/banner-white.svg
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
images/bedrock.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 236 KiB |
|
Before Width: | Height: | Size: 234 KiB |
8
main.go
@@ -15,8 +15,12 @@ package main
|
||||
|
||||
import "github.com/k8sgpt-ai/k8sgpt/cmd"
|
||||
|
||||
var version = "dev"
|
||||
var (
|
||||
version = "dev"
|
||||
commit = "HEAD"
|
||||
date = "unknown"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd.Execute(version)
|
||||
cmd.Execute(version, commit, date)
|
||||
}
|
||||
|
||||
196
pkg/ai/amazonbedrock.go
Normal file
@@ -0,0 +1,196 @@
|
||||
package ai
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/bedrockruntime"
|
||||
)
|
||||
|
||||
// AmazonBedRockClient represents the client for interacting with the Amazon Bedrock service.
|
||||
type AmazonBedRockClient struct {
|
||||
client *bedrockruntime.BedrockRuntime
|
||||
language string
|
||||
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 and language.
|
||||
func (a *AmazonBedRockClient) Configure(config IAIConfig, language string) 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.language = language
|
||||
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, promptTmpl 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
|
||||
}
|
||||
|
||||
// Parse generates a completion for the provided prompt using the Amazon Bedrock model.
|
||||
func (a *AmazonBedRockClient) Parse(ctx context.Context, prompt []string, cache cache.ICache, promptTmpl string) (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, promptTmpl)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// GetName returns the name of the AmazonBedRockClient.
|
||||
func (a *AmazonBedRockClient) GetName() string {
|
||||
return "amazonbedrock"
|
||||
}
|
||||
170
pkg/ai/amazonsagemaker.go
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
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/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"encoding/json"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/sagemakerruntime"
|
||||
)
|
||||
|
||||
type SageMakerAIClient struct {
|
||||
client *sagemakerruntime.SageMakerRuntime
|
||||
language string
|
||||
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, language string) error {
|
||||
|
||||
// Create a new AWS session
|
||||
sess := session.Must(session.NewSessionWithOptions(session.Options{
|
||||
Config: aws.Config{Region: aws.String(config.GetProviderRegion())},
|
||||
SharedConfigState: session.SharedConfigEnable,
|
||||
}))
|
||||
|
||||
c.language = language
|
||||
// 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(ctx context.Context, prompt string, promptTmpl string) (string, error) {
|
||||
// Create a completion request
|
||||
|
||||
if len(promptTmpl) == 0 {
|
||||
promptTmpl = PromptMap["default"]
|
||||
}
|
||||
|
||||
request := Request{
|
||||
Inputs: [][]Message{
|
||||
{
|
||||
{Role: "system", Content: "DEFAULT_PROMPT"},
|
||||
{Role: "user", Content: fmt.Sprintf(promptTmpl, c.language, 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 (a *SageMakerAIClient) Parse(ctx context.Context, prompt []string, cache cache.ICache, promptTmpl string) (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, promptTmpl)
|
||||
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 "", err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (a *SageMakerAIClient) GetName() string {
|
||||
return "amazonsagemaker"
|
||||
}
|
||||
106
pkg/ai/azureopenai.go
Normal file
@@ -0,0 +1,106 @@
|
||||
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/fatih/color"
|
||||
|
||||
"github.com/sashabaranov/go-openai"
|
||||
)
|
||||
|
||||
type AzureAIClient struct {
|
||||
client *openai.Client
|
||||
language string
|
||||
model string
|
||||
temperature float32
|
||||
}
|
||||
|
||||
func (c *AzureAIClient) Configure(config IAIConfig, lang string) 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.language = lang
|
||||
c.client = client
|
||||
c.model = config.GetModel()
|
||||
c.temperature = config.GetTemperature()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *AzureAIClient) GetCompletion(ctx context.Context, prompt string, promptTmpl 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: fmt.Sprintf(default_prompt, c.language, prompt),
|
||||
},
|
||||
},
|
||||
Temperature: c.temperature,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resp.Choices[0].Message.Content, nil
|
||||
}
|
||||
|
||||
func (a *AzureAIClient) Parse(ctx context.Context, prompt []string, cache cache.ICache, promptTmpl string) (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, promptTmpl)
|
||||
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 *AzureAIClient) GetName() string {
|
||||
return "azureopenai"
|
||||
}
|
||||
118
pkg/ai/cohere.go
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
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/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cohere-ai/cohere-go"
|
||||
"github.com/fatih/color"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||
)
|
||||
|
||||
type CohereClient struct {
|
||||
client *cohere.Client
|
||||
language string
|
||||
model string
|
||||
temperature float32
|
||||
}
|
||||
|
||||
func (c *CohereClient) Configure(config IAIConfig, language string) 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.language = language
|
||||
c.client = client
|
||||
c.model = config.GetModel()
|
||||
c.temperature = config.GetTemperature()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CohereClient) GetCompletion(ctx context.Context, prompt, promptTmpl string) (string, error) {
|
||||
// Create a completion request
|
||||
if len(promptTmpl) == 0 {
|
||||
promptTmpl = PromptMap["default"]
|
||||
}
|
||||
resp, err := c.client.Generate(cohere.GenerateOptions{
|
||||
Model: c.model,
|
||||
Prompt: fmt.Sprintf(strings.TrimSpace(promptTmpl), c.language, 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 (a *CohereClient) Parse(ctx context.Context, prompt []string, cache cache.ICache, promptTmpl string) (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, promptTmpl)
|
||||
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 *CohereClient) GetName() string {
|
||||
return "cohere"
|
||||
}
|
||||
@@ -19,10 +19,31 @@ import (
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
|
||||
)
|
||||
|
||||
var (
|
||||
clients = []IAI{
|
||||
&OpenAIClient{},
|
||||
&AzureAIClient{},
|
||||
&LocalAIClient{},
|
||||
&NoOpAIClient{},
|
||||
&CohereClient{},
|
||||
&AmazonBedRockClient{},
|
||||
&SageMakerAIClient{},
|
||||
}
|
||||
Backends = []string{
|
||||
"openai",
|
||||
"localai",
|
||||
"azureopenai",
|
||||
"noopai",
|
||||
"cohere",
|
||||
"amazonbedrock",
|
||||
"amazonsagemaker",
|
||||
}
|
||||
)
|
||||
|
||||
type IAI interface {
|
||||
Configure(config IAIConfig, language string) error
|
||||
GetCompletion(ctx context.Context, prompt string) (string, error)
|
||||
Parse(ctx context.Context, prompt []string, cache cache.ICache) (string, error)
|
||||
GetCompletion(ctx context.Context, prompt string, promptTmpl string) (string, error)
|
||||
Parse(ctx context.Context, prompt []string, cache cache.ICache, promptTmpl string) (string, error)
|
||||
GetName() string
|
||||
}
|
||||
|
||||
@@ -30,34 +51,58 @@ 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 {
|
||||
switch provider {
|
||||
case "openai":
|
||||
return &OpenAIClient{}
|
||||
case "noopai":
|
||||
return &NoOpAIClient{}
|
||||
default:
|
||||
return &OpenAIClient{}
|
||||
for _, c := range clients {
|
||||
if provider == c.GetName() {
|
||||
return c
|
||||
}
|
||||
}
|
||||
// default client
|
||||
return &OpenAIClient{}
|
||||
}
|
||||
|
||||
type AIConfiguration struct {
|
||||
Providers []AIProvider `mapstructure:"providers"`
|
||||
Providers []AIProvider `mapstructure:"providers"`
|
||||
DefaultProvider string `mapstructure:"defaultprovider"`
|
||||
}
|
||||
|
||||
type AIProvider struct {
|
||||
Name string `mapstructure:"name"`
|
||||
Model string `mapstructure:"model"`
|
||||
Password string `mapstructure:"password"`
|
||||
BaseURL string `mapstructure:"base_url"`
|
||||
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"`
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -65,3 +110,25 @@ func (p *AIProvider) GetPassword() string {
|
||||
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
|
||||
}
|
||||
|
||||
9
pkg/ai/localai.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package ai
|
||||
|
||||
type LocalAIClient struct {
|
||||
OpenAIClient
|
||||
}
|
||||
|
||||
func (a *LocalAIClient) GetName() string {
|
||||
return "localai"
|
||||
}
|
||||
@@ -38,26 +38,26 @@ func (c *NoOpAIClient) Configure(config IAIConfig, language string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *NoOpAIClient) GetCompletion(ctx context.Context, prompt string) (string, error) {
|
||||
func (c *NoOpAIClient) GetCompletion(ctx context.Context, prompt string, promptTmpl string) (string, error) {
|
||||
// Create a completion request
|
||||
response := "I am a noop response to the prompt " + prompt
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (a *NoOpAIClient) Parse(ctx context.Context, prompt []string, cache cache.ICache) (string, error) {
|
||||
func (a *NoOpAIClient) Parse(ctx context.Context, prompt []string, cache cache.ICache, promptTmpl string) (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)
|
||||
response, err := a.GetCompletion(ctx, inputKey, promptTmpl)
|
||||
if err != nil {
|
||||
color.Red("error getting completion: %v", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = cache.Store(cacheKey, base64.StdEncoding.EncodeToString([]byte(response)))
|
||||
err = cache.Store(cacheKey, base64.StdEncoding.EncodeToString([]byte(response)))
|
||||
|
||||
if err != nil {
|
||||
color.Red("error storing value to cache: %v", err)
|
||||
|
||||
@@ -29,11 +29,20 @@ import (
|
||||
)
|
||||
|
||||
type OpenAIClient struct {
|
||||
client *openai.Client
|
||||
language string
|
||||
model string
|
||||
client *openai.Client
|
||||
language string
|
||||
model string
|
||||
temperature float32
|
||||
}
|
||||
|
||||
const (
|
||||
// OpenAI completion parameters
|
||||
maxToken = 2048
|
||||
presencePenalty = 0.0
|
||||
frequencyPenalty = 0.0
|
||||
topP = 1.0
|
||||
)
|
||||
|
||||
func (c *OpenAIClient) Configure(config IAIConfig, language string) error {
|
||||
token := config.GetPassword()
|
||||
defaultConfig := openai.DefaultConfig(token)
|
||||
@@ -50,19 +59,28 @@ func (c *OpenAIClient) Configure(config IAIConfig, language string) error {
|
||||
c.language = language
|
||||
c.client = client
|
||||
c.model = config.GetModel()
|
||||
c.temperature = config.GetTemperature()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *OpenAIClient) GetCompletion(ctx context.Context, prompt string) (string, error) {
|
||||
func (c *OpenAIClient) GetCompletion(ctx context.Context, prompt string, promptTmpl string) (string, error) {
|
||||
// Create a completion request
|
||||
if len(promptTmpl) == 0 {
|
||||
promptTmpl = PromptMap["default"]
|
||||
}
|
||||
resp, err := c.client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{
|
||||
Model: c.model,
|
||||
Messages: []openai.ChatCompletionMessage{
|
||||
{
|
||||
Role: "user",
|
||||
Content: fmt.Sprintf(default_prompt, c.language, prompt),
|
||||
Content: fmt.Sprintf(promptTmpl, c.language, prompt),
|
||||
},
|
||||
},
|
||||
Temperature: c.temperature,
|
||||
MaxTokens: maxToken,
|
||||
PresencePenalty: presencePenalty,
|
||||
FrequencyPenalty: frequencyPenalty,
|
||||
TopP: topP,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -70,33 +88,28 @@ func (c *OpenAIClient) GetCompletion(ctx context.Context, prompt string) (string
|
||||
return resp.Choices[0].Message.Content, nil
|
||||
}
|
||||
|
||||
func (a *OpenAIClient) Parse(ctx context.Context, prompt []string, cache cache.ICache) (string, error) {
|
||||
func (a *OpenAIClient) Parse(ctx context.Context, prompt []string, cache cache.ICache, promptTmpl string) (string, error) {
|
||||
inputKey := strings.Join(prompt, " ")
|
||||
// Check for cached data
|
||||
sEnc := base64.StdEncoding.EncodeToString([]byte(inputKey))
|
||||
cacheKey := util.GetCacheKey(a.GetName(), a.language, sEnc)
|
||||
// find in viper cache
|
||||
if cache.Exists(cacheKey) {
|
||||
// retrieve data from cache
|
||||
response, err := cache.Load(cacheKey)
|
||||
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 == "" {
|
||||
color.Red("error retrieving cached data")
|
||||
return "", nil
|
||||
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
|
||||
}
|
||||
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)
|
||||
response, err := a.GetCompletion(ctx, inputKey, promptTmpl)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
package ai
|
||||
|
||||
const (
|
||||
default_prompt = "Simplify the following Kubernetes error message and provide a solution in %s: %s"
|
||||
prompt_a = "Read the following input %s and provide possible scenarios for remediation in %s"
|
||||
prompt_b = "Considering the following input from the Kubernetes resource %s and the error message %s, provide possible scenarios for remediation in %s"
|
||||
prompt_c = "Reading the following %s error message and it's accompanying log message %s, how would you simplify this message?"
|
||||
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."
|
||||
)
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"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"
|
||||
@@ -34,18 +35,22 @@ import (
|
||||
)
|
||||
|
||||
type Analysis struct {
|
||||
Context context.Context
|
||||
Filters []string
|
||||
Client *kubernetes.Client
|
||||
AIClient ai.IAI
|
||||
Results []common.Result
|
||||
Namespace string
|
||||
Cache cache.ICache
|
||||
Explain bool
|
||||
MaxConcurrency int
|
||||
Context context.Context
|
||||
Filters []string
|
||||
Client *kubernetes.Client
|
||||
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
|
||||
}
|
||||
|
||||
type AnalysisStatus string
|
||||
type AnalysisErrors []string
|
||||
|
||||
const (
|
||||
StateOK AnalysisStatus = "OK"
|
||||
@@ -53,12 +58,14 @@ 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) (*Analysis, error) {
|
||||
func NewAnalysis(backend string, language string, filters []string, namespace string, noCache bool, explain bool, maxConcurrency int, withDoc bool) (*Analysis, error) {
|
||||
var configAI ai.AIConfiguration
|
||||
err := viper.UnmarshalKey("ai", &configAI)
|
||||
if err != nil {
|
||||
@@ -71,6 +78,12 @@ func NewAnalysis(backend string, language string, filters []string, namespace st
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
var aiProvider ai.AIProvider
|
||||
for _, provider := range configAI.Providers {
|
||||
if backend == provider.Name {
|
||||
@@ -101,37 +114,60 @@ func NewAnalysis(backend string, language string, filters []string, namespace st
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// load remote cache if it is configured
|
||||
cache, err := cache.GetCacheConfiguration()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if noCache {
|
||||
cache.DisableCache()
|
||||
}
|
||||
|
||||
return &Analysis{
|
||||
Context: ctx,
|
||||
Filters: filters,
|
||||
Client: client,
|
||||
AIClient: aiClient,
|
||||
Namespace: namespace,
|
||||
Cache: cache.New(noCache),
|
||||
Explain: explain,
|
||||
MaxConcurrency: maxConcurrency,
|
||||
Context: ctx,
|
||||
Filters: filters,
|
||||
Client: client,
|
||||
AIClient: aiClient,
|
||||
Namespace: namespace,
|
||||
Cache: cache,
|
||||
Explain: explain,
|
||||
MaxConcurrency: maxConcurrency,
|
||||
AnalysisAIProvider: backend,
|
||||
WithDoc: withDoc,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *Analysis) RunAnalysis() []error {
|
||||
func (a *Analysis) RunAnalysis() {
|
||||
activeFilters := viper.GetStringSlice("active_filters")
|
||||
|
||||
analyzerMap := analyzer.GetAnalyzerMap()
|
||||
coreAnalyzerMap, analyzerMap := analyzer.GetAnalyzerMap()
|
||||
|
||||
analyzerConfig := common.Analyzer{
|
||||
Client: a.Client,
|
||||
Context: a.Context,
|
||||
Namespace: a.Namespace,
|
||||
AIClient: a.AIClient,
|
||||
// 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))
|
||||
}
|
||||
}
|
||||
|
||||
analyzerConfig := common.Analyzer{
|
||||
Client: a.Client,
|
||||
Context: a.Context,
|
||||
Namespace: a.Namespace,
|
||||
AIClient: a.AIClient,
|
||||
OpenapiSchema: openapiSchema,
|
||||
}
|
||||
|
||||
var errorList []error
|
||||
semaphore := make(chan struct{}, a.MaxConcurrency)
|
||||
// if there are no filters selected and no active_filters then run all of them
|
||||
// if there are no filters selected and no active_filters then run coreAnalyzer
|
||||
if len(a.Filters) == 0 && len(activeFilters) == 0 {
|
||||
var wg sync.WaitGroup
|
||||
var mutex sync.Mutex
|
||||
for _, analyzer := range analyzerMap {
|
||||
for _, analyzer := range coreAnalyzerMap {
|
||||
wg.Add(1)
|
||||
semaphore <- struct{}{}
|
||||
go func(analyzer common.IAnalyzer, wg *sync.WaitGroup, semaphore chan struct{}) {
|
||||
@@ -139,7 +175,7 @@ func (a *Analysis) RunAnalysis() []error {
|
||||
results, err := analyzer.Analyze(analyzerConfig)
|
||||
if err != nil {
|
||||
mutex.Lock()
|
||||
errorList = append(errorList, fmt.Errorf(fmt.Sprintf("[%s] %s", reflect.TypeOf(analyzer).Name(), err)))
|
||||
a.Errors = append(a.Errors, fmt.Sprintf("[%s] %s", reflect.TypeOf(analyzer).Name(), err))
|
||||
mutex.Unlock()
|
||||
}
|
||||
mutex.Lock()
|
||||
@@ -150,7 +186,7 @@ func (a *Analysis) RunAnalysis() []error {
|
||||
|
||||
}
|
||||
wg.Wait()
|
||||
return errorList
|
||||
return
|
||||
}
|
||||
semaphore = make(chan struct{}, a.MaxConcurrency)
|
||||
// if the filters flag is specified
|
||||
@@ -166,7 +202,7 @@ func (a *Analysis) RunAnalysis() []error {
|
||||
results, err := analyzer.Analyze(analyzerConfig)
|
||||
if err != nil {
|
||||
mutex.Lock()
|
||||
errorList = append(errorList, fmt.Errorf(fmt.Sprintf("[%s] %s", filter, err)))
|
||||
a.Errors = append(a.Errors, fmt.Sprintf("[%s] %s", filter, err))
|
||||
mutex.Unlock()
|
||||
}
|
||||
mutex.Lock()
|
||||
@@ -175,11 +211,11 @@ func (a *Analysis) RunAnalysis() []error {
|
||||
<-semaphore
|
||||
}(analyzer, filter)
|
||||
} else {
|
||||
errorList = append(errorList, fmt.Errorf(fmt.Sprintf("\"%s\" filter does not exist. Please run k8sgpt filters list.", filter)))
|
||||
a.Errors = append(a.Errors, fmt.Sprintf("\"%s\" filter does not exist. Please run k8sgpt filters list.", filter))
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
return errorList
|
||||
return
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
@@ -195,7 +231,7 @@ func (a *Analysis) RunAnalysis() []error {
|
||||
results, err := analyzer.Analyze(analyzerConfig)
|
||||
if err != nil {
|
||||
mutex.Lock()
|
||||
errorList = append(errorList, fmt.Errorf("[%s] %s", filter, err))
|
||||
a.Errors = append(a.Errors, fmt.Sprintf("[%s] %s", filter, err))
|
||||
mutex.Unlock()
|
||||
}
|
||||
mutex.Lock()
|
||||
@@ -206,7 +242,6 @@ func (a *Analysis) RunAnalysis() []error {
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
return errorList
|
||||
}
|
||||
|
||||
func (a *Analysis) GetAIResults(output string, anonymize bool) error {
|
||||
@@ -230,12 +265,19 @@ func (a *Analysis) GetAIResults(output string, anonymize bool) error {
|
||||
}
|
||||
texts = append(texts, failure.Text)
|
||||
}
|
||||
parsedText, err := a.AIClient.Parse(a.Context, texts, a.Cache)
|
||||
// If the resource `Kind` comes from a "integration plugin", maybe a customized prompt template will be involved.
|
||||
var promptTemplate string
|
||||
if prompt, ok := ai.PromptMap[analysis.Kind]; ok {
|
||||
promptTemplate = prompt
|
||||
} else {
|
||||
promptTemplate = ai.PromptMap["default"]
|
||||
}
|
||||
parsedText, err := a.AIClient.Parse(a.Context, texts, a.Cache, promptTemplate)
|
||||
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
|
||||
@@ -256,7 +298,7 @@ func (a *Analysis) GetAIResults(output string, anonymize bool) error {
|
||||
|
||||
analysis.Details = parsedText
|
||||
if output != "json" {
|
||||
bar.Add(1)
|
||||
_ = bar.Add(1)
|
||||
}
|
||||
a.Results[index] = analysis
|
||||
}
|
||||
|
||||
@@ -14,14 +14,127 @@ limitations under the License.
|
||||
package analysis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// 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{
|
||||
@@ -50,6 +163,7 @@ func TestAnalysis_NoProblemJsonOutput(t *testing.T) {
|
||||
fmt.Println(expected)
|
||||
|
||||
require.Equal(t, got, expected)
|
||||
|
||||
}
|
||||
|
||||
func TestAnalysis_ProblemJsonOutput(t *testing.T) {
|
||||
|
||||
@@ -42,8 +42,10 @@ 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, "", " ")
|
||||
@@ -55,6 +57,17 @@ func (a *Analysis) jsonOutput() ([]byte, error) {
|
||||
|
||||
func (a *Analysis) textOutput() ([]byte, error) {
|
||||
var output strings.Builder
|
||||
|
||||
// Print the AI provider used for this analysis
|
||||
output.WriteString(fmt.Sprintf("AI Provider: %s\n", color.YellowString(a.AnalysisAIProvider)))
|
||||
|
||||
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"))
|
||||
@@ -65,6 +78,9 @@ 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"))
|
||||
}
|
||||
|
||||
@@ -32,21 +32,27 @@ 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{},
|
||||
"Pod": PodAnalyzer{},
|
||||
"Deployment": DeploymentAnalyzer{},
|
||||
"ReplicaSet": ReplicaSetAnalyzer{},
|
||||
"PersistentVolumeClaim": PvcAnalyzer{},
|
||||
"Service": ServiceAnalyzer{},
|
||||
"Ingress": IngressAnalyzer{},
|
||||
"StatefulSet": StatefulSetAnalyzer{},
|
||||
"CronJob": CronJobAnalyzer{},
|
||||
"Node": NodeAnalyzer{},
|
||||
"ValidatingWebhookConfiguration": ValidatingWebhookAnalyzer{},
|
||||
"MutatingWebhookConfiguration": MutatingWebhookAnalyzer{},
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -71,25 +77,27 @@ 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 {
|
||||
func GetAnalyzerMap() (map[string]common.IAnalyzer, map[string]common.IAnalyzer) {
|
||||
|
||||
mergedMap := make(map[string]common.IAnalyzer)
|
||||
coreAnalyzer := make(map[string]common.IAnalyzer)
|
||||
mergedAnalyzerMap := make(map[string]common.IAnalyzer)
|
||||
|
||||
// add core analyzer
|
||||
for key, value := range coreAnalyzerMap {
|
||||
mergedMap[key] = value
|
||||
coreAnalyzer[key] = value
|
||||
mergedAnalyzerMap[key] = value
|
||||
}
|
||||
|
||||
// add additional analyzer
|
||||
for key, value := range additionalAnalyzerMap {
|
||||
mergedMap[key] = value
|
||||
mergedAnalyzerMap[key] = value
|
||||
}
|
||||
|
||||
integrationProvider := integration.NewIntegration()
|
||||
@@ -106,9 +114,9 @@ func GetAnalyzerMap() map[string]common.IAnalyzer {
|
||||
fmt.Println(color.RedString(err.Error()))
|
||||
os.Exit(1)
|
||||
}
|
||||
in.AddAnalyzer(&mergedMap)
|
||||
in.AddAnalyzer(&mergedAnalyzerMap)
|
||||
}
|
||||
}
|
||||
|
||||
return mergedMap
|
||||
return coreAnalyzer, mergedAnalyzerMap
|
||||
}
|
||||
|
||||
@@ -18,9 +18,11 @@ 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{}
|
||||
@@ -28,16 +30,22 @@ 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,
|
||||
})
|
||||
|
||||
var results []common.Result
|
||||
|
||||
cronJobList, err := a.Client.GetClient().BatchV1().CronJobs(a.Namespace).List(a.Context, v1.ListOptions{})
|
||||
if err != nil {
|
||||
return results, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var preAnalysis = map[string]common.PreAnalysis{}
|
||||
@@ -45,8 +53,11 @@ 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),
|
||||
Text: fmt.Sprintf("CronJob %s is suspended", cronJob.Name),
|
||||
KubernetesDoc: doc,
|
||||
Sensitive: []common.Sensitive{
|
||||
{
|
||||
Unmasked: cronJob.Namespace,
|
||||
@@ -61,8 +72,11 @@ 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()),
|
||||
Text: fmt.Sprintf("CronJob %s has an invalid schedule: %s", cronJob.Name, err.Error()),
|
||||
KubernetesDoc: doc,
|
||||
Sensitive: []common.Sensitive{
|
||||
{
|
||||
Unmasked: cronJob.Namespace,
|
||||
@@ -80,9 +94,11 @@ 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),
|
||||
Text: fmt.Sprintf("CronJob %s has a negative starting deadline", cronJob.Name),
|
||||
KubernetesDoc: doc,
|
||||
Sensitive: []common.Sensitive{
|
||||
{
|
||||
Unmasked: cronJob.Namespace,
|
||||
@@ -114,7 +130,7 @@ func (analyzer CronJobAnalyzer) Analyze(a common.Analyzer) ([]common.Result, err
|
||||
Name: key,
|
||||
Error: value.FailureDetails,
|
||||
}
|
||||
a.Results = append(results, currentAnalysis)
|
||||
a.Results = append(a.Results, currentAnalysis)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,10 @@ 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"
|
||||
)
|
||||
|
||||
@@ -31,6 +33,14 @@ 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,
|
||||
@@ -45,8 +55,11 @@ 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),
|
||||
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,
|
||||
Sensitive: []common.Sensitive{
|
||||
{
|
||||
Unmasked: deployment.Namespace,
|
||||
|
||||
108
pkg/analyzer/gateway.go
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
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 := >wapi.GatewayList{}
|
||||
gc := >wapi.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
|
||||
}
|
||||
161
pkg/analyzer/gateway_test.go
Normal file
@@ -0,0 +1,161 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
84
pkg/analyzer/gatewayclass.go
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
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 := >wapi.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
|
||||
}
|
||||
51
pkg/analyzer/gatewayclass_test.go
Normal file
@@ -0,0 +1,51 @@
|
||||
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 := >wapi.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)
|
||||
|
||||
}
|
||||
@@ -17,10 +17,12 @@ 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{}
|
||||
@@ -28,6 +30,14 @@ 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,
|
||||
@@ -76,8 +86,11 @@ 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),
|
||||
Text: fmt.Sprintf("HorizontalPodAutoscaler uses %s/%s as ScaleTargetRef which does not exist.", scaleTargetRef.Kind, scaleTargetRef.Name),
|
||||
KubernetesDoc: doc,
|
||||
Sensitive: []common.Sensitive{
|
||||
{
|
||||
Unmasked: scaleTargetRef.Name,
|
||||
@@ -94,8 +107,11 @@ 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),
|
||||
Text: fmt.Sprintf("%s %s/%s does not have resource configured.", scaleTargetRef.Kind, a.Namespace, scaleTargetRef.Name),
|
||||
KubernetesDoc: doc,
|
||||
Sensitive: []common.Sensitive{
|
||||
{
|
||||
Unmasked: scaleTargetRef.Name,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
228
pkg/analyzer/httroute.go
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
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 := >wapi.HTTPRouteList{}
|
||||
gtw := >wapi.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
|
||||
|
||||
}
|
||||
374
pkg/analyzer/httroute_test.go
Normal file
@@ -0,0 +1,374 @@
|
||||
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 := >wapi.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: >wapi.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: >wNamespace,
|
||||
},
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -17,8 +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"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
type IngressAnalyzer struct{}
|
||||
@@ -26,6 +28,14 @@ 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,
|
||||
@@ -46,8 +56,11 @@ 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),
|
||||
Text: fmt.Sprintf("Ingress %s/%s does not specify an Ingress class.", ing.Namespace, ing.Name),
|
||||
KubernetesDoc: doc,
|
||||
Sensitive: []common.Sensitive{
|
||||
{
|
||||
Unmasked: ing.Namespace,
|
||||
@@ -68,8 +81,11 @@ 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),
|
||||
Text: fmt.Sprintf("Ingress uses the ingress class %s which does not exist.", *ingressClassName),
|
||||
KubernetesDoc: doc,
|
||||
Sensitive: []common.Sensitive{
|
||||
{
|
||||
Unmasked: *ingressClassName,
|
||||
@@ -82,23 +98,28 @@ func (IngressAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
|
||||
|
||||
// loop over rules
|
||||
for _, rule := range ing.Spec.Rules {
|
||||
// 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),
|
||||
// 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),
|
||||
},
|
||||
},
|
||||
{
|
||||
Unmasked: path.Backend.Service.Name,
|
||||
Masked: util.MaskString(path.Backend.Service.Name),
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,8 +127,11 @@ 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),
|
||||
Text: fmt.Sprintf("Ingress uses the secret %s/%s as a TLS certificate which does not exist.", ing.Namespace, tls.SecretName),
|
||||
KubernetesDoc: doc,
|
||||
Sensitive: []common.Sensitive{
|
||||
{
|
||||
Unmasked: ing.Namespace,
|
||||
|
||||
116
pkg/analyzer/log.go
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
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(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(line) {
|
||||
return line
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
155
pkg/analyzer/mutating_webhook.go
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
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
|
||||
}
|
||||
@@ -17,8 +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"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
type NetworkPolicyAnalyzer struct{}
|
||||
@@ -26,6 +28,14 @@ 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,
|
||||
@@ -45,8 +55,11 @@ 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),
|
||||
Text: fmt.Sprintf("Network policy allows traffic to all pods: %s", policy.Name),
|
||||
KubernetesDoc: doc,
|
||||
Sensitive: []common.Sensitive{
|
||||
{
|
||||
Unmasked: policy.Name,
|
||||
@@ -54,23 +67,23 @@ func (NetworkPolicyAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error)
|
||||
},
|
||||
},
|
||||
})
|
||||
continue
|
||||
}
|
||||
// Check if policy is not applied to any pods
|
||||
podList, err := util.GetPodListByLabels(a.Client.GetClient(), a.Namespace, policy.Spec.PodSelector.MatchLabels)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(podList.Items) == 0 {
|
||||
failures = append(failures, common.Failure{
|
||||
Text: fmt.Sprintf("Network policy is not applied to any pods: %s", policy.Name),
|
||||
Sensitive: []common.Sensitive{
|
||||
{
|
||||
Unmasked: policy.Name,
|
||||
Masked: util.MaskString(policy.Name),
|
||||
} else {
|
||||
// Check if policy is not applied to any pods
|
||||
podList, err := util.GetPodListByLabels(a.Client.GetClient(), a.Namespace, policy.Spec.PodSelector.MatchLabels)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(podList.Items) == 0 {
|
||||
failures = append(failures, common.Failure{
|
||||
Text: fmt.Sprintf("Network policy is not applied to any pods: %s", policy.Name),
|
||||
Sensitive: []common.Sensitive{
|
||||
{
|
||||
Unmasked: policy.Name,
|
||||
Masked: util.MaskString(policy.Name),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if len(failures) > 0 {
|
||||
|
||||
@@ -58,7 +58,7 @@ func (NodeAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
|
||||
}
|
||||
|
||||
if len(failures) > 0 {
|
||||
preAnalysis[fmt.Sprintf("%s", node.Name)] = common.PreAnalysis{
|
||||
preAnalysis[node.Name] = common.PreAnalysis{
|
||||
Node: node,
|
||||
FailureDetails: failures,
|
||||
}
|
||||
|
||||
@@ -17,8 +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"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
type PdbAnalyzer struct{}
|
||||
@@ -26,6 +28,14 @@ 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,
|
||||
@@ -40,17 +50,19 @@ func (PdbAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
|
||||
|
||||
for _, pdb := range list.Items {
|
||||
var failures []common.Failure
|
||||
|
||||
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 {
|
||||
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 {
|
||||
for k, v := range pdb.Spec.Selector.MatchLabels {
|
||||
failures = append(failures, common.Failure{
|
||||
Text: fmt.Sprintf("%s, expected label %s=%s", evt.Message, k, v),
|
||||
Text: fmt.Sprintf("%s, expected pdb pod label %s=%s", pdb.Status.Conditions[0].Reason, k, v),
|
||||
KubernetesDoc: doc,
|
||||
Sensitive: []common.Sensitive{
|
||||
{
|
||||
Unmasked: k,
|
||||
@@ -63,17 +75,6 @@ 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{},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,15 +59,16 @@ 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 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{},
|
||||
})
|
||||
}
|
||||
|
||||
if isErrorReason(containerStatus.State.Waiting.Reason) && 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" {
|
||||
|
||||
@@ -85,7 +86,7 @@ func (PodAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
|
||||
}
|
||||
} else {
|
||||
// when pod is Running but its ReadinessProbe fails
|
||||
if containerStatus.Ready == false && pod.Status.Phase == "Running" {
|
||||
if !containerStatus.Ready && 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 {
|
||||
@@ -125,3 +126,16 @@ 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
|
||||
}
|
||||
|
||||
@@ -17,9 +17,13 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||
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"
|
||||
)
|
||||
|
||||
type ServiceAnalyzer struct{}
|
||||
@@ -27,6 +31,14 @@ 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,
|
||||
@@ -45,6 +57,10 @@ 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)
|
||||
@@ -52,8 +68,11 @@ 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),
|
||||
Text: fmt.Sprintf("Service has no endpoints, expected label %s=%s", k, v),
|
||||
KubernetesDoc: doc,
|
||||
Sensitive: []common.Sensitive{
|
||||
{
|
||||
Unmasked: k,
|
||||
@@ -72,14 +91,20 @@ 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),
|
||||
Sensitive: []common.Sensitive{},
|
||||
Text: fmt.Sprintf("Service has not ready endpoints, pods: %s, expected %d", pods, count),
|
||||
KubernetesDoc: doc,
|
||||
Sensitive: []common.Sensitive{},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +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"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
type StatefulSetAnalyzer struct{}
|
||||
@@ -26,6 +28,14 @@ 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,
|
||||
@@ -44,8 +54,15 @@ 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),
|
||||
Text: fmt.Sprintf(
|
||||
"StatefulSet uses the service %s/%s which does not exist.",
|
||||
sts.Namespace,
|
||||
serviceName,
|
||||
),
|
||||
KubernetesDoc: doc,
|
||||
Sensitive: []common.Sensitive{
|
||||
{
|
||||
Unmasked: sts.Namespace,
|
||||
|
||||
153
pkg/analyzer/validating_webhook.go
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
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
|
||||
}
|
||||
155
pkg/cache/azuresa_based.go
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
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
|
||||
}
|
||||
121
pkg/cache/cache.go
vendored
@@ -1,15 +1,128 @@
|
||||
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(noCache bool) ICache {
|
||||
if noCache {
|
||||
return &NoopCache{}
|
||||
func New(cacheType string) ICache {
|
||||
for _, t := range types {
|
||||
if cacheType == t.GetName() {
|
||||
return t
|
||||
}
|
||||
}
|
||||
|
||||
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{}
|
||||
}
|
||||
|
||||
cache.Configure(cacheInfo)
|
||||
|
||||
return cache, nil
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
60
pkg/cache/file_based.go
vendored
@@ -11,7 +11,43 @@ import (
|
||||
|
||||
var _ (ICache) = (*FileBasedCache)(nil)
|
||||
|
||||
type FileBasedCache struct{}
|
||||
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))
|
||||
@@ -47,6 +83,20 @@ 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))
|
||||
|
||||
@@ -56,3 +106,11 @@ 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
|
||||
}
|
||||
|
||||