Compare commits

..

105 Commits

Author SHA1 Message Date
github-actions[bot]
ea7f0a5b4e chore(main): release 0.3.28 (#964)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-03-14 10:15:49 +00:00
Vaibhav Malik
531f0bc46d test: added tests for the Service analyzer (#1011)
* Added new tests for the `Service` analyzer defined in the
  `pkg/analyzer` package.

* The addition of these new tests has increased the code coverage of the
  service.go file to over 97%.

* Additionally addressed some flaky tests related to the `ReplicaSet`and
  `PersisentVolumeClaim` analyzers.

Partially addresses: https://github.com/k8sgpt-ai/k8sgpt/issues/889

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
Co-authored-by: Aris Boutselis <arisboutselis08@gmail.com>
2024-03-14 09:42:16 +00:00
Vaibhav Malik
28e19a9d4e test: added tests for the pkg/kubernetes package (#896)
Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
2024-03-12 21:34:12 +00:00
Vaibhav Malik
3475e2de0c test: fixed various flaky tests (#1009)
- Removed test cases which required access to `/root` from the
  `pkg/util` package.

- Fixed flaky `PodDisruptionBudget` test.

- Fixed a typo in `PersistentVolumeClaim` test.

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
2024-03-12 07:25:03 +00:00
Aris Boutselis
adf4f17085 chore: attempt to group renovate deps (#1007) 2024-03-11 17:48:18 +00:00
Mario
55ac0b2129 feat: add Google Vertex AI as provider to utilize gemini via GCP (#984)
* feat: add Google Vertex AI as provider to utilize gemini via GCP

Signed-off-by: Mario Fahlandt <mfahlandt@pixel-haufen.de>

* fix: adjust providerId description

Signed-off-by: Mario Fahlandt <mfahlandt@pixel-haufen.de>

---------

Signed-off-by: Mario Fahlandt <mfahlandt@pixel-haufen.de>
Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
Co-authored-by: Aris Boutselis <arisboutselis08@gmail.com>
2024-03-11 07:33:29 +00:00
Vaibhav Malik
a0225d4f70 test: added tests for the PVC analyzer (#1000)
This commit introduces comprehensive tests for the
`PersistentVolumeClaim` analyzer defined in the `pkg/analyzer` package.

Adding these tests increases the code coverage of the `pvc.go` file to
>95%.

I also made minor modifications to the ReplicaSet test to ensure all
expectations were met.

Partially addresses: https://github.com/k8sgpt-ai/k8sgpt/issues/889

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
2024-03-08 19:26:19 +00:00
renovate[bot]
b05b6a38ed chore(deps): update anchore/sbom-action action to v0.15.9 (#1004)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-08 11:21:34 +00:00
Vaibhav Malik
1340ead860 test: added tests for the PDB analyzer (#1001)
This commit introduces comprehensive tests for the `PodDisruptionBudget`
analyzer defined in the `pkg/analyzer` package.

Adding these tests increases the code coverage of the `pdb.go` file to
>96%.

Additionally, a potential crash in case of empty or nil PDB status
conditions has been addressed.

Partially addresses: https://github.com/k8sgpt-ai/k8sgpt/issues/889

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
2024-03-08 11:09:14 +00:00
renovate[bot]
b58b7191af chore(deps): update docker/build-push-action digest to af5a7ed (#1003)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-08 11:08:46 +00:00
renovate[bot]
1491e67567 fix(deps): update module github.com/stretchr/testify to v1.9.0 (#999)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2024-03-08 09:37:23 +00:00
renovate[bot]
4ec143ab77 chore(deps): update reviewdog/action-golangci-lint digest to 00311c2 (#1002)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-08 09:36:58 +00:00
Vaibhav Malik
5199dadb2a test: added tests for mutating webhook analyzer (#995)
This commit introduces comprehensive tests for the mutating webhook
analyzer defined in the `pkg/analyzer` package.

Adding these tests increases the code coverage of the
`mutating_webhook.go` file to almost 95%.

Partially addresses: https://github.com/k8sgpt-ai/k8sgpt/issues/889

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
2024-03-08 08:18:20 +00:00
renovate[bot]
425f33bb2d fix(deps): update module github.com/aws/aws-sdk-go to v1.50.34 (#974)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-08 08:12:15 +00:00
Vaibhav Malik
f5c3f18d87 test: added tests for the ReplicaSet analyzer (#997)
This commit introduces comprehensive tests for the ReplicaSet analyzer
defined in the `pkg/analyzer` package.

Adding these tests increases the code coverage of the `rs.go` file to
>95%.

Partially addresses: https://github.com/k8sgpt-ai/k8sgpt/issues/889

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
2024-03-05 19:39:31 +00:00
renovate[bot]
d2754d320f fix(deps): update module github.com/sashabaranov/go-openai to v1.20.2 (#991)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-05 14:04:04 +00:00
renovate[bot]
85f18dde1f fix(deps): update module github.com/azure/azure-sdk-for-go/sdk/storage/azblob to v1.3.1 (#992)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-05 13:57:29 +00:00
Vaibhav Malik
16a4aaab81 test: added tests for validating webhook analyzer (#996)
This commit introduces comprehensive tests for the validating webhook
analyzer defined in the `pkg/analyzer` package.

Adding these tests increases the code coverage of the
`validating_webhook.go` file to almost 95%.

Partially addresses: https://github.com/k8sgpt-ai/k8sgpt/issues/889

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
2024-03-05 13:36:55 +00:00
renovate[bot]
4065faef13 fix(deps): update module github.com/prometheus/client_golang to v1.19.0 (#989)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-28 16:52:50 +00:00
renovate[bot]
f24bcd88b6 chore(deps): update docker/setup-buildx-action digest to 0d103c3 (#988)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-28 16:40:38 +00:00
Tanuj Dwivedi
307710eddc feat: add proxysettings for azureopenai and openai (#987)
Signed-off-by: tanujd11 <dwiveditanuj41@gmail.com>
Co-authored-by: Aris Boutselis <arisboutselis08@gmail.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2024-02-28 16:10:42 +00:00
Vaibhav Malik
aab8d77feb fix: analyze command default backend bug (#966)
Now, the default value of the `backend` flag for the analyze command
will be an empty string. And the `NewAnalysis` function has been
modified to use the default backend set by the user if the backend flag
is not provided and the `defaultprovider` is set in the config file.
Otherwise, backend will be set to "openai".

Fixes: https://github.com/k8sgpt-ai/k8sgpt/issues/902

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
Co-authored-by: JuHyung Son <sonju0427@gmail.com>
2024-02-28 16:09:30 +00:00
renovate[bot]
334a86aaf4 fix(deps): update module gopkg.in/yaml.v2 to v3 (#980)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-27 19:50:30 +00:00
renovate[bot]
88a7907db4 fix(deps): update module github.com/sashabaranov/go-openai to v1.20.1 (#986)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-27 19:39:10 +00:00
renovate[bot]
af3732ad06 fix(deps): update module github.com/schollz/progressbar/v3 to v3.14.2 (#983)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-26 18:47:52 +00:00
Alex Jones
a81377f72d feat: aws integration (#967)
* chore: updated deps

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

* chore: adding aws types

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

* chore: first cut

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

* chore: first pass at aws integration with EKS

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

* chore: fixed linting

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

* chore: updated wording based on PR

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

* chore: improved the kubeconfig

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

---------

Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
2024-02-26 10:16:32 +00:00
renovate[bot]
6103c96c41 fix(deps): update module google.golang.org/api to v0.167.0 (#973)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-25 17:34:36 +00:00
renovate[bot]
35f5185914 fix(deps): update module gopkg.in/yaml.v2 to v3 (#979)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-23 11:00:30 +00:00
renovate[bot]
97446aae07 fix(deps): update module google.golang.org/grpc to v1.62.0 (#975)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-23 08:28:25 +00:00
renovate[bot]
e07822c10b fix(deps): update module github.com/sashabaranov/go-openai to v1.20.0 (#977)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-23 08:05:22 +00:00
renovate[bot]
f929e7feea fix(deps): update module gopkg.in/yaml.v2 to v3 (#957)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-22 17:48:25 +00:00
Vaibhav Malik
6e640e6921 test: added unit tests for the pkg/util package (#894)
This commit adds new unit tests for the `pkg/util` package bumping the
code coverage to 84%

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2024-02-22 16:21:33 +00:00
lili-wan
98286a965e fix: log analyzer failed with multiple containers in the pod (#920)
* Log analyzer failed with multiple containers in the pod #884

Signed-off-by: lwan3 <lili_wan@intuit.com>

* Merge conflicts from main

Signed-off-by: lwan3 <lili_wan@intuit.com>

---------

Signed-off-by: lwan3 <lili_wan@intuit.com>
Co-authored-by: lwan3 <lili_wan@intuit.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2024-02-21 12:33:44 +00:00
renovate[bot]
6ac815c10f fix(deps): update module github.com/aws/aws-sdk-go to v1.50.22 (#971)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-21 12:27:49 +00:00
renovate[bot]
8f00218090 fix(deps): update module go.uber.org/zap to v1.27.0 (#972)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-21 08:51:54 +00:00
renovate[bot]
00c91f05a6 fix(deps): update module github.com/aws/aws-sdk-go to v1.50.21 (#970)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-20 20:48:13 +00:00
renovate[bot]
6207c70c51 fix(deps): update module cloud.google.com/go/storage to v1.38.0 (#950)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-19 21:14:38 +00:00
renovate[bot]
8b0b61e596 fix(deps): update module github.com/sashabaranov/go-openai to v1.19.4 (#963)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-19 11:45:37 +00:00
renovate[bot]
248260e081 fix(deps): update module github.com/google/generative-ai-go to v0.8.0 (#965)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-19 08:10:12 +00:00
Vaibhav Malik
f55f8370eb fix: shorthand for the http flag in serve command (#969)
Removed the shorthand for the `http` flag in the serve command because
it was contradicting with the shorthand of the `help` command which is
automatically added on execution if the `help` flag is not already
defined.

Fixes: https://github.com/k8sgpt-ai/k8sgpt/issues/968

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
2024-02-19 07:48:16 +00:00
Johannes Kleinlercher
a3cd7e6385 fix: set result name and namespace to trivy vulnreport and configaudi… (#869)
* fix: set result name and namespace to trivy vulnreport and configauditreport

Signed-off-by: Johannes Kleinlercher <johannes.kleinlercher@suxess-it.com>

* fix: increase linter timeout

Signed-off-by: Johannes Kleinlercher <johannes@kleinlercher.at>

---------

Signed-off-by: Johannes Kleinlercher <johannes.kleinlercher@suxess-it.com>
Signed-off-by: Johannes Kleinlercher <johannes@kleinlercher.at>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2024-02-18 15:47:19 +00:00
Sahil Badla
f2138c7101 feat: enable Rest api using grpc-gateway (#834)
* grpc-gateway changes

Signed-off-by: Sahil Badla <sahil_badla@intuit.com>

* feat: grpc-gateway impl

Signed-off-by: Sahil Badla <sahil_badla@intuit.com>

* feat: enable REST/http api support

Signed-off-by: Sahil Badla <sahil_badla@intuit.com>

* feat: enable rest/http support

Signed-off-by: Sahil Badla <sahil_badla@intuit.com>

* feat: enable rest/http support

Signed-off-by: Sahil Badla <sahil_badla@intuit.com>

* feat: enable rest/http support

Signed-off-by: Sahil Badla <sahil_badla@intuit.com>

* chore: resolved mod

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

* feat: fix grpc-gateway codegen path

Signed-off-by: Sahil Badla <sahil_badla@intuit.com>

* feat: merge from master

Signed-off-by: Sahil Badla <sahil_badla@intuit.com>

* feat: flag to enable rest api

Signed-off-by: Sahil Badla <sahil_badla@intuit.com>

---------

Signed-off-by: Sahil Badla <sahil_badla@intuit.com>
Signed-off-by: Sahil Badla <146279034+sbadla1@users.noreply.github.com>
Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
Co-authored-by: Sahil Badla <sahil_badla@intuit.com>
Co-authored-by: Thomas Schuetz <38893055+thschue@users.noreply.github.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2024-02-17 15:38:15 +00:00
renovate[bot]
3f0356be66 fix(deps): update module github.com/aws/aws-sdk-go to v1.50.20 (#930)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-17 08:43:26 +00:00
renovate[bot]
cc99bd51f0 fix(deps): update module google.golang.org/api to v0.165.0 (#959)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-16 14:07:23 +00:00
github-actions[bot]
729d14db4d chore(main): release 0.3.27 (#870)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-02-16 12:53:55 +00:00
renovate[bot]
fea2ed1fff fix(deps): update module github.com/azure/azure-sdk-for-go/sdk/storage/azblob to v1.3.0 (#952)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-15 12:48:17 +00:00
Alex Jones
c8c9dbfadc feat: enables remote custom analyzers (#906)
* feat: enables remote custom analyzers

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

* chore: fixed test that was broken

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

* chore: hiding custom analysis behind a flag

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

* chore: resolved govet issue

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

* chore: updated

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

* chore: updated

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

* chore: updated deps

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

* chore: updated deps

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

---------

Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
2024-02-15 07:28:00 +00:00
renovate[bot]
070aa7fdd0 chore(deps): update actions/upload-artifact digest to 5d5d22a (#925)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-14 19:35:47 +00:00
renovate[bot]
ce7c9551bc fix(deps): update module github.com/azure/azure-sdk-for-go/sdk/azidentity to v1.5.1 (#939)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2024-02-14 12:33:02 +00:00
renovate[bot]
d9fe7446af fix(deps): update module gopkg.in/yaml.v2 to v3 (#956)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-14 11:01:22 +00:00
renovate[bot]
9c1f1b8804 fix(deps): update module google.golang.org/grpc to v1.61.1 (#954)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-14 10:48:40 +00:00
renovate[bot]
37228d88e3 chore(deps): update golang docker tag to v1.22 (#931)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-14 10:22:39 +00:00
renovate[bot]
29b482f597 fix(deps): update module google.golang.org/api to v0.164.0 (#953)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-14 08:26:35 +00:00
Alex Jones
015bccfc2e chore: updated deps (#951)
Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
2024-02-13 20:45:33 +00:00
Amit Amrutiya
3f0964ad38 fix: unused variable failure warning in webhooks file (#916)
Signed-off-by: amitamrutiya2210 <amitamrutiya2210@gmail.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2024-02-13 20:28:11 +00:00
renovate[bot]
3c8d9d42e5 fix(deps): update module github.com/google/generative-ai-go to v0.7.0 (#940)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-13 19:59:37 +00:00
renovate[bot]
bfbb5c7e03 fix(deps): update module github.com/pterm/pterm to v0.12.79 (#943)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-13 15:19:27 +00:00
renovate[bot]
28c4c57e45 fix(deps): update k8s.io/utils digest to e7106e6 (#897)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-13 15:09:48 +00:00
renovate[bot]
4e57088a01 fix(deps): update module golang.org/x/term to v0.17.0 (#941)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-09 22:10:45 +00:00
renovate[bot]
f2eb1ef533 fix(deps): update module github.com/sashabaranov/go-openai to v1.19.3 (#937)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-09 12:41:58 +00:00
Alex Jones
bbf61f53d4 chore: grpc update (#938)
Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
2024-02-09 09:42:00 +00:00
renovate[bot]
3d2554b9cd fix(deps): update module cloud.google.com/go/storage to v1.37.0 (#934)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-09 09:25:55 +00:00
renovate[bot]
f61c3e228c chore(deps): update anchore/sbom-action action to v0.15.8 (#926)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-08 09:35:32 +01:00
renovate[bot]
c6019728ae fix(deps): update module github.com/sashabaranov/go-openai to v1.19.2 (#886)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-08 08:25:47 +00:00
renovate[bot]
e3eee6d956 fix(deps): update module buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go to v1.3.0-20240128172516-6bf6a55ff115.2 (#899)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-07 18:43:30 +00:00
renovate[bot]
599be33f38 chore(deps): update reviewdog/action-golangci-lint digest to 8e1117c (#915)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-07 15:44:45 +00:00
Amit Amrutiya
3415031006 fix: lint errors (#923)
Signed-off-by: amitamrutiya2210 <amitamrutiya2210@gmail.com>
2024-02-07 11:30:45 +00:00
simone ragonesi
d97dea2896 chore: improve codebase and doc quality (#922)
* chore(prometheus integration): fix mispelling

Signed-off-by: r3drun3 <simone.ragonesi@sighup.io>

* docs(readme): add badges to improve doc quality

Signed-off-by: r3drun3 <simone.ragonesi@sighup.io>

---------

Signed-off-by: r3drun3 <simone.ragonesi@sighup.io>
2024-02-07 09:12:41 +00:00
renovate[bot]
f9c1b90338 fix(deps): update module github.com/pterm/pterm to v0.12.78 (#890)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-05 07:38:34 +00:00
lili-wan
78126b2328 feat: added FailedMount event reason to get the failure (#883)
Signed-off-by: lwan3 <lili_wan@intuit.com>
Co-authored-by: lwan3 <lili_wan@intuit.com>
2024-02-01 18:57:20 +00:00
renovate[bot]
60853fe4eb chore(deps): update anchore/sbom-action action to v0.15.5 (#885)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-29 12:18:39 +00:00
renovate[bot]
a253af23b6 fix(deps): update module github.com/prometheus/prometheus to v2 (#863)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-29 11:21:22 +00:00
JuHyung Son
2fd476e126 feat: add huggingface provider (#893)
* feat: add huggingface ai provider

Signed-off-by: JuHyung-Son <sonju0427@gmail.com>

* chore: update readme

Signed-off-by: JuHyung-Son <sonju0427@gmail.com>

* fix: set huggingface maxtokens default to 500, use ptr instead of pointer

Signed-off-by: JuHyung-Son <sonju0427@gmail.com>

---------

Signed-off-by: JuHyung-Son <sonju0427@gmail.com>
2024-01-28 15:41:49 +00:00
renovate[bot]
483a9dad10 chore(deps): update actions/upload-artifact digest to 26f96df (#888)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-24 14:09:03 +00:00
renovate[bot]
817d9cf754 fix(deps): update module github.com/aws/aws-sdk-go to v1.50.2 (#887)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-24 10:55:01 +00:00
renovate[bot]
72e08efff1 fix(deps): update module google.golang.org/api to v0.157.0 (#860)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-23 13:12:44 +00:00
renovate[bot]
e7d690afd1 fix(deps): update module github.com/pterm/pterm to v0.12.75 (#881)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-23 08:06:44 +00:00
renovate[bot]
3cf18e783e chore(deps): update actions/upload-artifact digest to 694cdab (#880)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-23 08:02:18 +00:00
souleb
cdbeb146a2 fix: typo in httproute files name (#877)
Signed-off-by: Soule BA <bah.soule@gmail.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2024-01-23 07:51:57 +00:00
Alex Jones
2effbb345a chore: linting improvements and catching false positives (#882)
* chore: linting improvements and catching false positives

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

* chore: linting improvements and catching false positives

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

* chore: linting improvements and catching false positives

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

* chore: increase linter time out

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

---------

Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
2024-01-23 07:46:01 +00:00
renovate[bot]
335616c20f chore(deps): update reviewdog/action-golangci-lint digest to f016e79 (#714)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-22 07:36:28 +00:00
renovate[bot]
d213399161 chore(deps): update anchore/sbom-action action to v0.15.4 (#879)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-22 07:35:27 +00:00
renovate[bot]
1f371e2807 fix(deps): update module gopkg.in/yaml.v2 to v3 (#875)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-18 16:52:31 +00:00
renovate[bot]
4de1bbd6f7 fix(deps): update module github.com/sashabaranov/go-openai to v1.18.2 (#874)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-16 12:43:49 +00:00
renovate[bot]
81d660447d fix(deps): update module gopkg.in/yaml.v2 to v3 (#866)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-16 09:56:21 +00:00
Philip Miglinci
a34f5dea69 ci: replace deprecated brew config (#873)
tap got replaces by repository and is now deprecated.

This deprecation warning can also be seen in the goreleaser logs:

>     • DEPRECATED: brews.tap should not be used anymore, check https://goreleaser.com/deprecations#brewstap for more info


https://github.com/k8sgpt-ai/k8sgpt/actions/runs/7521548656/job/20472514099

Signed-off-by: Philip Miglinci <pmig@glasskube.eu>
Co-authored-by: Thomas Schuetz <38893055+thschue@users.noreply.github.com>
2024-01-16 10:35:36 +01:00
renovate[bot]
6c62c1a0fc fix(deps): update module github.com/sashabaranov/go-openai to v1.18.1 (#871)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-16 09:30:32 +00:00
Philip Miglinci
42be51bc8f chore: set correct license during package build (#872)
Signed-off-by: Philip Miglinci <pmig@glasskube.eu>
2024-01-16 08:23:49 +00:00
renovate[bot]
88002e7e8c fix(deps): update module github.com/aws/aws-sdk-go to v1.49.21 (#868)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-15 19:21:58 +00:00
github-actions[bot]
6d3a3933cd chore(main): release 0.3.26 (#846)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-01-14 20:28:11 +00:00
Alex Jones
9da75e02bc feat: interactive mode (#854)
* chore: wip interactive mode

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

* chore: tidied up a bit

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

* chore: updated go mod

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

* chore: const prompt

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

* chore: updated based on comments feedback

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

* chore: updated enum

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

* chore: updated enum

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

---------

Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
2024-01-14 20:22:00 +00:00
renovate[bot]
4ce56f38b4 chore(deps): update actions/upload-artifact digest to 1eb3cb2 (#867)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-14 20:08:49 +00:00
renovate[bot]
40b5b7e185 fix(deps): update module github.com/sashabaranov/go-openai to v1.17.11 (#861)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-12 16:41:19 +00:00
renovate[bot]
c55025d04e fix(deps): update module gopkg.in/yaml.v2 to v3 (#865)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-12 14:39:52 +00:00
renovate[bot]
36ba6c5147 fix(deps): update module gopkg.in/yaml.v2 to v3 (#864)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-12 14:27:06 +00:00
renovate[bot]
6a2f315b2f fix(deps): update module github.com/aws/aws-sdk-go to v1.49.19 (#859)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-12 12:56:45 +00:00
Daniel Clark
45fa827c04 feat: initial Prometheus analyzers (#855)
* feat: initial Prometheus analyzers

Added a prometheus integration with two analyzers:
1. PrometheusConfigValidate
2. PrometheusConfigRelabelReport

The integration does not deploy any Prometheus stack in the cluster.
Instead, it searches the provided --namespace for a Prometheus
configuration, stored in a ConfigMap or Secret. If it finds one, it
unmarshals it into memory and runs the analyzers on it.

PrometheusConfigValidate checks if the actual Prometheus configuration is valid or has
any errors.

PrometheusConfigRelabelReport tries to distill the scrape config
relabeling rules to give a concise label set per job that targets need
to have to be scraped. This analyzer is unconventional, in that it does
not necessarily mean there are issues with the config. It merely tries
to give a human-readable explanation of the relabel rules it discovers,
leaning on the LLM and prompt.

Tested on both kube-prometheus and Google Managed Prometheus
stacks.

Signed-off-by: Daniel Clark <danielclark@google.com>

* review: feedback cycle 1

Simplify ConfigValidate prompt and add comments.

Signed-off-by: Daniel Clark <danielclark@google.com>

* review: feedback cycle 2

Add Prometheus configuration discovery to integration activate command.

Also improve logging to make this more clear to users.

Signed-off-by: Daniel Clark <danielclark@google.com>

---------

Signed-off-by: Daniel Clark <danielclark@google.com>
2024-01-12 09:58:09 +00:00
renovate[bot]
4106d39c32 fix(deps): update module github.com/aws/aws-sdk-go to v1.49.18 (#856)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-11 08:44:46 +00:00
renovate[bot]
1979c86d0f fix(deps): update module github.com/sashabaranov/go-openai to v1.17.11 (#853)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-09 21:03:14 +01:00
renovate[bot]
12f764d584 chore(deps): update anchore/sbom-action action to v0.15.3 (#850)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-09 09:21:21 +00:00
renovate[bot]
85ebd12c30 fix(deps): update module github.com/aws/aws-sdk-go to v1.49.17 (#852)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-09 08:44:58 +00:00
Matthis
5c17c24055 feat: unify aiClientName const for all providers (#848) 2024-01-07 13:01:15 +01:00
renovate[bot]
ce4910bc5d fix(deps): update module github.com/aws/aws-sdk-go to v1.49.16 (#847)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-05 22:49:23 +01:00
Anais Urlichs
d8d0beef65 make cli more intuitive when adding openai default backend (#838)
Signed-off-by: AnaisUrlichs <urlichsanais@gmail.com>
Co-authored-by: Thomas Schuetz <38893055+thschue@users.noreply.github.com>
2024-01-05 22:43:08 +01:00
Bartlomiej Plotka
745e960f49 docs: adjusted README information about providers (#844)
Mentioned the basics and referred to website for more details to keep ~one source of truth.

Signed-off-by: bwplotka <bwplotka@gmail.com>
2024-01-05 22:30:34 +01:00
69 changed files with 4517 additions and 619 deletions

View File

@@ -74,10 +74,10 @@ jobs:
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3
uses: docker/setup-buildx-action@0d103c3126aa41d772a8362f6aa67afac040f80c # v3
- name: Build Docker Image
uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5
uses: docker/build-push-action@af5a7ed5ba88268d5278f7203fb52cd833f66d6e # 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@c7d193f32edcb7bfad88892161225aeda64e9392 # v4
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4
with:
name: ${{ env.IMAGE_NAME }}-image.tar
path: /tmp/${{ env.IMAGE_NAME }}-image.tar
@@ -126,10 +126,10 @@ jobs:
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3
uses: docker/setup-buildx-action@0d103c3126aa41d772a8362f6aa67afac040f80c # v3
- name: Build Docker Image
uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5
uses: docker/build-push-action@af5a7ed5ba88268d5278f7203fb52cd833f66d6e # v5
with:
context: .
file: ./container/Dockerfile

View File

@@ -2,7 +2,7 @@ name: Run golangci-lint
on:
pull_request:
branches: [ main ]
branches: [main]
jobs:
golangci-lint:
@@ -12,7 +12,9 @@ jobs:
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- name: golangci-lint
uses: reviewdog/action-golangci-lint@24d4af2fc93f5b2b296229e8b0c0f658d25707af # v2
uses: reviewdog/action-golangci-lint@00311c26a97213f93f2fd3a3524d66762e956ae0 # v2
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
reporter: github-pr-check
golangci_lint_flags: "--timeout=240s"
level: warning

View File

@@ -49,7 +49,7 @@ jobs:
with:
go-version: '1.21'
- name: Download Syft
uses: anchore/sbom-action/download-syft@719133684c7d294116626d1344fe64f0d2ff3e9e # v0.15.2
uses: anchore/sbom-action/download-syft@9fece9e20048ca9590af301449208b2b8861333b # v0.15.9
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5
with:
@@ -80,7 +80,7 @@ jobs:
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3
uses: docker/setup-buildx-action@0d103c3126aa41d772a8362f6aa67afac040f80c # v3
- name: Login to GitHub Container Registry
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3
@@ -90,7 +90,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker Image
uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5
uses: docker/build-push-action@af5a7ed5ba88268d5278f7203fb52cd833f66d6e # 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@719133684c7d294116626d1344fe64f0d2ff3e9e # v0.15.2
uses: anchore/sbom-action@9fece9e20048ca9590af301449208b2b8861333b # v0.15.9
with:
image: ${{ env.IMAGE_TAG }}
artifact-name: sbom-${{ env.IMAGE_NAME }}

View File

@@ -23,7 +23,7 @@ nfpms:
homepage: https://k8sgpt.ai
description: >-
K8sGPT is a tool for scanning your kubernetes clusters, diagnosing and triaging issues in simple english. It has SRE experience codified into its analyzers and helps to pull out the most relevant information to enrich it with AI.
license: "MIT"
license: "Apache-2.0"
formats:
- deb
- rpm
@@ -57,7 +57,7 @@ archives:
brews:
- name: k8sgpt
homepage: https://k8sgpt.ai
tap:
repository:
owner: k8sgpt-ai
name: homebrew-k8sgpt

View File

@@ -1 +1 @@
{".":"0.3.25"}
{".":"0.3.28"}

View File

@@ -1,5 +1,142 @@
# Changelog
## [0.3.28](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.27...v0.3.28) (2024-03-14)
### Features
* add Google Vertex AI as provider to utilize gemini via GCP ([#984](https://github.com/k8sgpt-ai/k8sgpt/issues/984)) ([55ac0b2](https://github.com/k8sgpt-ai/k8sgpt/commit/55ac0b2129a438661a0253251f546db6b59f2b92))
* add proxysettings for azureopenai and openai ([#987](https://github.com/k8sgpt-ai/k8sgpt/issues/987)) ([307710e](https://github.com/k8sgpt-ai/k8sgpt/commit/307710eddc1c3f96f40a674f7dda786510e9c4cc))
* aws integration ([#967](https://github.com/k8sgpt-ai/k8sgpt/issues/967)) ([a81377f](https://github.com/k8sgpt-ai/k8sgpt/commit/a81377f72db7f322e0afbb6d613c2bfffecf8080))
* enable Rest api using grpc-gateway ([#834](https://github.com/k8sgpt-ai/k8sgpt/issues/834)) ([f2138c7](https://github.com/k8sgpt-ai/k8sgpt/commit/f2138c71017b391625eebdfb4c5708c824824f69))
### Bug Fixes
* analyze command default backend bug ([#966](https://github.com/k8sgpt-ai/k8sgpt/issues/966)) ([aab8d77](https://github.com/k8sgpt-ai/k8sgpt/commit/aab8d77febdd4b42ff74aafbb2ada27745c04ae1))
* **deps:** update module cloud.google.com/go/storage to v1.38.0 ([#950](https://github.com/k8sgpt-ai/k8sgpt/issues/950)) ([6207c70](https://github.com/k8sgpt-ai/k8sgpt/commit/6207c70c51d2885c4590c255c8f78e7ee2009034))
* **deps:** update module github.com/aws/aws-sdk-go to v1.50.20 ([#930](https://github.com/k8sgpt-ai/k8sgpt/issues/930)) ([3f0356b](https://github.com/k8sgpt-ai/k8sgpt/commit/3f0356be662c32d82ce4f3db05f859477823717d))
* **deps:** update module github.com/aws/aws-sdk-go to v1.50.21 ([#970](https://github.com/k8sgpt-ai/k8sgpt/issues/970)) ([00c91f0](https://github.com/k8sgpt-ai/k8sgpt/commit/00c91f05a62b2c8b2d756b58b95279195ff38d3d))
* **deps:** update module github.com/aws/aws-sdk-go to v1.50.22 ([#971](https://github.com/k8sgpt-ai/k8sgpt/issues/971)) ([6ac815c](https://github.com/k8sgpt-ai/k8sgpt/commit/6ac815c10fb073f4251e338ab22e247625f21406))
* **deps:** update module github.com/aws/aws-sdk-go to v1.50.34 ([#974](https://github.com/k8sgpt-ai/k8sgpt/issues/974)) ([425f33b](https://github.com/k8sgpt-ai/k8sgpt/commit/425f33bb2ddf8cdaff079b097d6956f675c89b0e))
* **deps:** update module github.com/azure/azure-sdk-for-go/sdk/storage/azblob to v1.3.1 ([#992](https://github.com/k8sgpt-ai/k8sgpt/issues/992)) ([85f18dd](https://github.com/k8sgpt-ai/k8sgpt/commit/85f18dde1f820fe2413cc6b3109e67b7a010142c))
* **deps:** update module github.com/google/generative-ai-go to v0.8.0 ([#965](https://github.com/k8sgpt-ai/k8sgpt/issues/965)) ([248260e](https://github.com/k8sgpt-ai/k8sgpt/commit/248260e081327de9f9d1d2c851efab2b4a3e7ede))
* **deps:** update module github.com/prometheus/client_golang to v1.19.0 ([#989](https://github.com/k8sgpt-ai/k8sgpt/issues/989)) ([4065fae](https://github.com/k8sgpt-ai/k8sgpt/commit/4065faef13691f9cf1f50696c62d3b30b0933b4b))
* **deps:** update module github.com/sashabaranov/go-openai to v1.19.4 ([#963](https://github.com/k8sgpt-ai/k8sgpt/issues/963)) ([8b0b61e](https://github.com/k8sgpt-ai/k8sgpt/commit/8b0b61e596f790b9558a5e3d1f634a5ee1c6cb0c))
* **deps:** update module github.com/sashabaranov/go-openai to v1.20.0 ([#977](https://github.com/k8sgpt-ai/k8sgpt/issues/977)) ([e07822c](https://github.com/k8sgpt-ai/k8sgpt/commit/e07822c10bff5dbd91f4da592914c25538353d6b))
* **deps:** update module github.com/sashabaranov/go-openai to v1.20.1 ([#986](https://github.com/k8sgpt-ai/k8sgpt/issues/986)) ([88a7907](https://github.com/k8sgpt-ai/k8sgpt/commit/88a7907db4700c241e9aa109bc3d8604a8186f87))
* **deps:** update module github.com/sashabaranov/go-openai to v1.20.2 ([#991](https://github.com/k8sgpt-ai/k8sgpt/issues/991)) ([d2754d3](https://github.com/k8sgpt-ai/k8sgpt/commit/d2754d320fb1f285f93fdced2b8469280bd47fd2))
* **deps:** update module github.com/schollz/progressbar/v3 to v3.14.2 ([#983](https://github.com/k8sgpt-ai/k8sgpt/issues/983)) ([af3732a](https://github.com/k8sgpt-ai/k8sgpt/commit/af3732ad067b809c54c5f08f6cf5a7a519b452d7))
* **deps:** update module github.com/stretchr/testify to v1.9.0 ([#999](https://github.com/k8sgpt-ai/k8sgpt/issues/999)) ([1491e67](https://github.com/k8sgpt-ai/k8sgpt/commit/1491e675673dcc13ccf6ac1778113762542e8cbc))
* **deps:** update module go.uber.org/zap to v1.27.0 ([#972](https://github.com/k8sgpt-ai/k8sgpt/issues/972)) ([8f00218](https://github.com/k8sgpt-ai/k8sgpt/commit/8f002180901c8bf7e6b1a5451dd97ef566260b0f))
* **deps:** update module google.golang.org/api to v0.165.0 ([#959](https://github.com/k8sgpt-ai/k8sgpt/issues/959)) ([cc99bd5](https://github.com/k8sgpt-ai/k8sgpt/commit/cc99bd51f05db4e87f806ac58ee1cb7a83b25e4d))
* **deps:** update module google.golang.org/api to v0.167.0 ([#973](https://github.com/k8sgpt-ai/k8sgpt/issues/973)) ([6103c96](https://github.com/k8sgpt-ai/k8sgpt/commit/6103c96c41e10e2fe13d285ff15a36bf2fbeb5c2))
* **deps:** update module google.golang.org/grpc to v1.62.0 ([#975](https://github.com/k8sgpt-ai/k8sgpt/issues/975)) ([97446aa](https://github.com/k8sgpt-ai/k8sgpt/commit/97446aae079824d6556416314c0a27514088a667))
* **deps:** update module gopkg.in/yaml.v2 to v3 ([#957](https://github.com/k8sgpt-ai/k8sgpt/issues/957)) ([f929e7f](https://github.com/k8sgpt-ai/k8sgpt/commit/f929e7feea5931ddec77af49dd08937aca85fd49))
* **deps:** update module gopkg.in/yaml.v2 to v3 ([#979](https://github.com/k8sgpt-ai/k8sgpt/issues/979)) ([35f5185](https://github.com/k8sgpt-ai/k8sgpt/commit/35f51859140c78ce953443afcc27f77230287809))
* **deps:** update module gopkg.in/yaml.v2 to v3 ([#980](https://github.com/k8sgpt-ai/k8sgpt/issues/980)) ([334a86a](https://github.com/k8sgpt-ai/k8sgpt/commit/334a86aaf40e5421929cf380191841db064d9bf7))
* log analyzer failed with multiple containers in the pod ([#920](https://github.com/k8sgpt-ai/k8sgpt/issues/920)) ([98286a9](https://github.com/k8sgpt-ai/k8sgpt/commit/98286a965e4c4c680deeb43d3397b51089968366))
* set result name and namespace to trivy vulnreport and configaudi… ([#869](https://github.com/k8sgpt-ai/k8sgpt/issues/869)) ([a3cd7e6](https://github.com/k8sgpt-ai/k8sgpt/commit/a3cd7e6385365a1d190a9e8439311cb9d5eeda56))
* shorthand for the http flag in serve command ([#969](https://github.com/k8sgpt-ai/k8sgpt/issues/969)) ([f55f837](https://github.com/k8sgpt-ai/k8sgpt/commit/f55f8370ebf0db6db629641337cd78ad7f120865))
### Other
* attempt to group renovate deps ([#1007](https://github.com/k8sgpt-ai/k8sgpt/issues/1007)) ([adf4f17](https://github.com/k8sgpt-ai/k8sgpt/commit/adf4f17085672fd5ae78dad4f8ac1d887029836d))
* **deps:** update anchore/sbom-action action to v0.15.9 ([#1004](https://github.com/k8sgpt-ai/k8sgpt/issues/1004)) ([b05b6a3](https://github.com/k8sgpt-ai/k8sgpt/commit/b05b6a38ed4a9fc017f9dcb52cff8a332c11056d))
* **deps:** update docker/build-push-action digest to af5a7ed ([#1003](https://github.com/k8sgpt-ai/k8sgpt/issues/1003)) ([b58b719](https://github.com/k8sgpt-ai/k8sgpt/commit/b58b7191af2fe082d94d46ef6a2784c1ea322340))
* **deps:** update docker/setup-buildx-action digest to 0d103c3 ([#988](https://github.com/k8sgpt-ai/k8sgpt/issues/988)) ([f24bcd8](https://github.com/k8sgpt-ai/k8sgpt/commit/f24bcd88b6a915798897b49a562b86265a9b524c))
* **deps:** update reviewdog/action-golangci-lint digest to 00311c2 ([#1002](https://github.com/k8sgpt-ai/k8sgpt/issues/1002)) ([4ec143a](https://github.com/k8sgpt-ai/k8sgpt/commit/4ec143ab772ca4dc3072c248e95da8f7c0a2974b))
## [0.3.27](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.26...v0.3.27) (2024-02-15)
### Features
* add huggingface provider ([#893](https://github.com/k8sgpt-ai/k8sgpt/issues/893)) ([2fd476e](https://github.com/k8sgpt-ai/k8sgpt/commit/2fd476e12624e30570c0819594f2668f720381d6))
* added FailedMount event reason to get the failure ([#883](https://github.com/k8sgpt-ai/k8sgpt/issues/883)) ([78126b2](https://github.com/k8sgpt-ai/k8sgpt/commit/78126b2328c1b3f81a269d203e86128104050010))
* enables remote custom analyzers ([#906](https://github.com/k8sgpt-ai/k8sgpt/issues/906)) ([c8c9dbf](https://github.com/k8sgpt-ai/k8sgpt/commit/c8c9dbfadc72a193ab9f3431d02d50ac5ab5d071))
### Bug Fixes
* **deps:** update k8s.io/utils digest to e7106e6 ([#897](https://github.com/k8sgpt-ai/k8sgpt/issues/897)) ([28c4c57](https://github.com/k8sgpt-ai/k8sgpt/commit/28c4c57e4566b9b888a5633090ccb70875d30106))
* **deps:** update module buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go to v1.3.0-20240128172516-6bf6a55ff115.2 ([#899](https://github.com/k8sgpt-ai/k8sgpt/issues/899)) ([e3eee6d](https://github.com/k8sgpt-ai/k8sgpt/commit/e3eee6d9566a59fd62e6bb804257b1383f75e3ef))
* **deps:** update module cloud.google.com/go/storage to v1.37.0 ([#934](https://github.com/k8sgpt-ai/k8sgpt/issues/934)) ([3d2554b](https://github.com/k8sgpt-ai/k8sgpt/commit/3d2554b9cd8817b24cf8858a107420d6d8424aa4))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.21 ([#868](https://github.com/k8sgpt-ai/k8sgpt/issues/868)) ([88002e7](https://github.com/k8sgpt-ai/k8sgpt/commit/88002e7e8c3e9c71365c44e136a6f1a8d35e1744))
* **deps:** update module github.com/aws/aws-sdk-go to v1.50.2 ([#887](https://github.com/k8sgpt-ai/k8sgpt/issues/887)) ([817d9cf](https://github.com/k8sgpt-ai/k8sgpt/commit/817d9cf754d307d374befc0d57919eb7a0183aaf))
* **deps:** update module github.com/azure/azure-sdk-for-go/sdk/azidentity to v1.5.1 ([#939](https://github.com/k8sgpt-ai/k8sgpt/issues/939)) ([ce7c955](https://github.com/k8sgpt-ai/k8sgpt/commit/ce7c9551bcb1a8b24922a1eb062605bbfeec7929))
* **deps:** update module github.com/azure/azure-sdk-for-go/sdk/storage/azblob to v1.3.0 ([#952](https://github.com/k8sgpt-ai/k8sgpt/issues/952)) ([fea2ed1](https://github.com/k8sgpt-ai/k8sgpt/commit/fea2ed1fff5fb5a46d6abc2feb72e1e1adf3b69b))
* **deps:** update module github.com/google/generative-ai-go to v0.7.0 ([#940](https://github.com/k8sgpt-ai/k8sgpt/issues/940)) ([3c8d9d4](https://github.com/k8sgpt-ai/k8sgpt/commit/3c8d9d42e573f27185a1572d1bc06f8af87f3a0b))
* **deps:** update module github.com/prometheus/prometheus to v2 ([#863](https://github.com/k8sgpt-ai/k8sgpt/issues/863)) ([a253af2](https://github.com/k8sgpt-ai/k8sgpt/commit/a253af23b601b23179be5019fbb832a41423cdae))
* **deps:** update module github.com/pterm/pterm to v0.12.75 ([#881](https://github.com/k8sgpt-ai/k8sgpt/issues/881)) ([e7d690a](https://github.com/k8sgpt-ai/k8sgpt/commit/e7d690afd12cb71d7b344ba92bf059ae18a993c8))
* **deps:** update module github.com/pterm/pterm to v0.12.78 ([#890](https://github.com/k8sgpt-ai/k8sgpt/issues/890)) ([f9c1b90](https://github.com/k8sgpt-ai/k8sgpt/commit/f9c1b903385978be56f9c4bc87089bd1c761bbea))
* **deps:** update module github.com/pterm/pterm to v0.12.79 ([#943](https://github.com/k8sgpt-ai/k8sgpt/issues/943)) ([bfbb5c7](https://github.com/k8sgpt-ai/k8sgpt/commit/bfbb5c7e03cad144f6037c7233ffc0817fd403e4))
* **deps:** update module github.com/sashabaranov/go-openai to v1.18.1 ([#871](https://github.com/k8sgpt-ai/k8sgpt/issues/871)) ([6c62c1a](https://github.com/k8sgpt-ai/k8sgpt/commit/6c62c1a0fcd38cf9de8a99cda6f37b221740b9c8))
* **deps:** update module github.com/sashabaranov/go-openai to v1.18.2 ([#874](https://github.com/k8sgpt-ai/k8sgpt/issues/874)) ([4de1bbd](https://github.com/k8sgpt-ai/k8sgpt/commit/4de1bbd6f72ca83d46ce5955bac50dffc99af03d))
* **deps:** update module github.com/sashabaranov/go-openai to v1.19.2 ([#886](https://github.com/k8sgpt-ai/k8sgpt/issues/886)) ([c601972](https://github.com/k8sgpt-ai/k8sgpt/commit/c6019728aea837884620e0b4894568802a948a6e))
* **deps:** update module github.com/sashabaranov/go-openai to v1.19.3 ([#937](https://github.com/k8sgpt-ai/k8sgpt/issues/937)) ([f2eb1ef](https://github.com/k8sgpt-ai/k8sgpt/commit/f2eb1ef5334877fd3a26dda8c92023f831ea857e))
* **deps:** update module golang.org/x/term to v0.17.0 ([#941](https://github.com/k8sgpt-ai/k8sgpt/issues/941)) ([4e57088](https://github.com/k8sgpt-ai/k8sgpt/commit/4e57088a0137767a42c778a59ff07fff04c04289))
* **deps:** update module google.golang.org/api to v0.157.0 ([#860](https://github.com/k8sgpt-ai/k8sgpt/issues/860)) ([72e08ef](https://github.com/k8sgpt-ai/k8sgpt/commit/72e08efff1fc501dfcba791c9d940e575f3e2395))
* **deps:** update module google.golang.org/api to v0.164.0 ([#953](https://github.com/k8sgpt-ai/k8sgpt/issues/953)) ([29b482f](https://github.com/k8sgpt-ai/k8sgpt/commit/29b482f5978795fa8db729030bd75803e2e61f95))
* **deps:** update module google.golang.org/grpc to v1.61.1 ([#954](https://github.com/k8sgpt-ai/k8sgpt/issues/954)) ([9c1f1b8](https://github.com/k8sgpt-ai/k8sgpt/commit/9c1f1b8804a26f549379efe637d0bedb8e2cb890))
* **deps:** update module gopkg.in/yaml.v2 to v3 ([#866](https://github.com/k8sgpt-ai/k8sgpt/issues/866)) ([81d6604](https://github.com/k8sgpt-ai/k8sgpt/commit/81d660447d236cd03b75866871bb69f2c77c5c66))
* **deps:** update module gopkg.in/yaml.v2 to v3 ([#875](https://github.com/k8sgpt-ai/k8sgpt/issues/875)) ([1f371e2](https://github.com/k8sgpt-ai/k8sgpt/commit/1f371e2807c47dbb4613bf873ec67a77e8e6c80c))
* **deps:** update module gopkg.in/yaml.v2 to v3 ([#956](https://github.com/k8sgpt-ai/k8sgpt/issues/956)) ([d9fe744](https://github.com/k8sgpt-ai/k8sgpt/commit/d9fe7446af428209610adc83ec17cf50491a5a47))
* lint errors ([#923](https://github.com/k8sgpt-ai/k8sgpt/issues/923)) ([3415031](https://github.com/k8sgpt-ai/k8sgpt/commit/3415031006bb5899019e68d33ac6083d03ef864b))
* typo in httproute files name ([#877](https://github.com/k8sgpt-ai/k8sgpt/issues/877)) ([cdbeb14](https://github.com/k8sgpt-ai/k8sgpt/commit/cdbeb146a28ebc21ac2c4d27e977b1771f9290b4))
* unused variable failure warning in webhooks file ([#916](https://github.com/k8sgpt-ai/k8sgpt/issues/916)) ([3f0964a](https://github.com/k8sgpt-ai/k8sgpt/commit/3f0964ad385390f53516904219fbfc47b989d31f))
### Other
* **deps:** update actions/upload-artifact digest to 26f96df ([#888](https://github.com/k8sgpt-ai/k8sgpt/issues/888)) ([483a9da](https://github.com/k8sgpt-ai/k8sgpt/commit/483a9dad103ad1af82491dc1d5e0a39bb4865a1b))
* **deps:** update actions/upload-artifact digest to 5d5d22a ([#925](https://github.com/k8sgpt-ai/k8sgpt/issues/925)) ([070aa7f](https://github.com/k8sgpt-ai/k8sgpt/commit/070aa7fdd0982c0c7f02a1da9e6797d5efaa5586))
* **deps:** update actions/upload-artifact digest to 694cdab ([#880](https://github.com/k8sgpt-ai/k8sgpt/issues/880)) ([3cf18e7](https://github.com/k8sgpt-ai/k8sgpt/commit/3cf18e783edb341b7bdd6aa20dbcce11971fa241))
* **deps:** update anchore/sbom-action action to v0.15.4 ([#879](https://github.com/k8sgpt-ai/k8sgpt/issues/879)) ([d213399](https://github.com/k8sgpt-ai/k8sgpt/commit/d2133991617697b13b8846f2acb3a3bb6cebb160))
* **deps:** update anchore/sbom-action action to v0.15.5 ([#885](https://github.com/k8sgpt-ai/k8sgpt/issues/885)) ([60853fe](https://github.com/k8sgpt-ai/k8sgpt/commit/60853fe4eb8de7a1fdbaea388c3d2d6205e273a6))
* **deps:** update anchore/sbom-action action to v0.15.8 ([#926](https://github.com/k8sgpt-ai/k8sgpt/issues/926)) ([f61c3e2](https://github.com/k8sgpt-ai/k8sgpt/commit/f61c3e228c69fa160735ddb2c1347720112b738f))
* **deps:** update golang docker tag to v1.22 ([#931](https://github.com/k8sgpt-ai/k8sgpt/issues/931)) ([37228d8](https://github.com/k8sgpt-ai/k8sgpt/commit/37228d88e357c66c5574559ae27a52fdf28418b8))
* **deps:** update reviewdog/action-golangci-lint digest to 8e1117c ([#915](https://github.com/k8sgpt-ai/k8sgpt/issues/915)) ([599be33](https://github.com/k8sgpt-ai/k8sgpt/commit/599be33f38ad1fd688b8e7824102a7944d516435))
* **deps:** update reviewdog/action-golangci-lint digest to f016e79 ([#714](https://github.com/k8sgpt-ai/k8sgpt/issues/714)) ([335616c](https://github.com/k8sgpt-ai/k8sgpt/commit/335616c20f7f8d9fefab4976d986a8d3b4867111))
* grpc update ([#938](https://github.com/k8sgpt-ai/k8sgpt/issues/938)) ([bbf61f5](https://github.com/k8sgpt-ai/k8sgpt/commit/bbf61f53d4fb9244b5a79ae953370296ca9fd44b))
* improve codebase and doc quality ([#922](https://github.com/k8sgpt-ai/k8sgpt/issues/922)) ([d97dea2](https://github.com/k8sgpt-ai/k8sgpt/commit/d97dea289681cd061ca0796208c50720bdb08914))
* linting improvements and catching false positives ([#882](https://github.com/k8sgpt-ai/k8sgpt/issues/882)) ([2effbb3](https://github.com/k8sgpt-ai/k8sgpt/commit/2effbb345ad1c2771ec798e06ccde68d3253b4bc))
* set correct license during package build ([#872](https://github.com/k8sgpt-ai/k8sgpt/issues/872)) ([42be51b](https://github.com/k8sgpt-ai/k8sgpt/commit/42be51bc8f625a35b1435c461d9a32c3c4905f1c))
* updated deps ([#951](https://github.com/k8sgpt-ai/k8sgpt/issues/951)) ([015bccf](https://github.com/k8sgpt-ai/k8sgpt/commit/015bccfc2eae587e0ade371211404f5af4c37d27))
## [0.3.26](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.25...v0.3.26) (2024-01-14)
### Features
* initial Prometheus analyzers ([#855](https://github.com/k8sgpt-ai/k8sgpt/issues/855)) ([45fa827](https://github.com/k8sgpt-ai/k8sgpt/commit/45fa827c046b91d901a08bec1a892d9c0917f350))
* interactive mode ([#854](https://github.com/k8sgpt-ai/k8sgpt/issues/854)) ([9da75e0](https://github.com/k8sgpt-ai/k8sgpt/commit/9da75e02bc17146898377e4f90b7f59c5a8e0eee))
* unify aiClientName const for all providers ([#848](https://github.com/k8sgpt-ai/k8sgpt/issues/848)) ([5c17c24](https://github.com/k8sgpt-ai/k8sgpt/commit/5c17c240550609d9fb7771fe67fe1ab19660b4da))
### Bug Fixes
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.16 ([#847](https://github.com/k8sgpt-ai/k8sgpt/issues/847)) ([ce4910b](https://github.com/k8sgpt-ai/k8sgpt/commit/ce4910bc5d064f80076877d7a096fff903308b63))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.17 ([#852](https://github.com/k8sgpt-ai/k8sgpt/issues/852)) ([85ebd12](https://github.com/k8sgpt-ai/k8sgpt/commit/85ebd12c30d369c5ef9a42b5a834d091523a7b6e))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.18 ([#856](https://github.com/k8sgpt-ai/k8sgpt/issues/856)) ([4106d39](https://github.com/k8sgpt-ai/k8sgpt/commit/4106d39c322940413ebfd9ac0bf6f5bd31830e93))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.19 ([#859](https://github.com/k8sgpt-ai/k8sgpt/issues/859)) ([6a2f315](https://github.com/k8sgpt-ai/k8sgpt/commit/6a2f315b2f4344f2924b7915e8a1393f9732a1e9))
* **deps:** update module github.com/sashabaranov/go-openai to v1.17.11 ([#853](https://github.com/k8sgpt-ai/k8sgpt/issues/853)) ([1979c86](https://github.com/k8sgpt-ai/k8sgpt/commit/1979c86d0f59921d55cd4229a37d604a6f1dc578))
* **deps:** update module github.com/sashabaranov/go-openai to v1.17.11 ([#861](https://github.com/k8sgpt-ai/k8sgpt/issues/861)) ([40b5b7e](https://github.com/k8sgpt-ai/k8sgpt/commit/40b5b7e185c8d335bdefb131988b9900ad26bac3))
* **deps:** update module gopkg.in/yaml.v2 to v3 ([#864](https://github.com/k8sgpt-ai/k8sgpt/issues/864)) ([36ba6c5](https://github.com/k8sgpt-ai/k8sgpt/commit/36ba6c5147a9ed75c14dbba4bc06cae903e651a4))
* **deps:** update module gopkg.in/yaml.v2 to v3 ([#865](https://github.com/k8sgpt-ai/k8sgpt/issues/865)) ([c55025d](https://github.com/k8sgpt-ai/k8sgpt/commit/c55025d04ebf9da0f6092aabb0b043ccef05164c))
### Other
* **deps:** update actions/upload-artifact digest to 1eb3cb2 ([#867](https://github.com/k8sgpt-ai/k8sgpt/issues/867)) ([4ce56f3](https://github.com/k8sgpt-ai/k8sgpt/commit/4ce56f38b4338a6a2fe69f588b0f17e0b54d0ae6))
* **deps:** update anchore/sbom-action action to v0.15.3 ([#850](https://github.com/k8sgpt-ai/k8sgpt/issues/850)) ([12f764d](https://github.com/k8sgpt-ai/k8sgpt/commit/12f764d5846accbd987d40f69a153dceb9954f39))
### Docs
* adjusted README information about providers ([#844](https://github.com/k8sgpt-ai/k8sgpt/issues/844)) ([745e960](https://github.com/k8sgpt-ai/k8sgpt/commit/745e960f492e6dd0e50aa4a1ce7239c677025024))
## [0.3.25](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.24...v0.3.25) (2024-01-05)

253
README.md
View File

@@ -8,15 +8,18 @@
![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/k8sgpt-ai/k8sgpt/release.yaml)
![GitHub release (latest by date)](https://img.shields.io/github/v/release/k8sgpt-ai/k8sgpt)
[![OpenSSF Best Practices](https://bestpractices.coreinfrastructure.org/projects/7272/badge)](https://bestpractices.coreinfrastructure.org/projects/7272)
[![Link to documentation](https://img.shields.io/static/v1?label=%F0%9F%93%96&message=Documentation&color=blue)](https://docs.k8sgpt.ai/)
[![Link to documentation](https://img.shields.io/static/v1?label=%F0%9F%93%96&message=Documentation&color=blue)](https://docs.k8sgpt.ai/)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fk8sgpt-ai%2Fk8sgpt.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fk8sgpt-ai%2Fk8sgpt?ref=badge_shield)
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![Go version](https://img.shields.io/github/go-mod/go-version/k8sgpt-ai/k8sgpt.svg)](https://github.com/k8sgpt-ai/k8sgpt)
[![codecov](https://codecov.io/github/k8sgpt-ai/k8sgpt/graph/badge.svg?token=ZLR7NG8URE)](https://codecov.io/github/k8sgpt-ai/k8sgpt)
![GitHub last commit (branch)](https://img.shields.io/github/last-commit/k8sgpt-ai/k8sgpt/main)
`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._
_Out of the box integration with OpenAI, Azure, Cohere, Amazon Bedrock, Google Gemini and local models._
<a href="https://www.producthunt.com/posts/k8sgpt?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-k8sgpt" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=389489&theme=light" alt="K8sGPT - K8sGPT&#0032;gives&#0032;Kubernetes&#0032;Superpowers&#0032;to&#0032;everyone | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
@@ -38,7 +41,7 @@ brew install k8sgpt
**32 bit:**
<!---x-release-please-start-version-->
```
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.25/k8sgpt_386.rpm
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.28/k8sgpt_386.rpm
sudo rpm -ivh k8sgpt_386.rpm
```
<!---x-release-please-end-->
@@ -47,7 +50,7 @@ brew install k8sgpt
<!---x-release-please-start-version-->
```
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.25/k8sgpt_amd64.rpm
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.28/k8sgpt_amd64.rpm
sudo rpm -ivh -i k8sgpt_amd64.rpm
```
<!---x-release-please-end-->
@@ -59,7 +62,7 @@ brew install k8sgpt
**32 bit:**
<!---x-release-please-start-version-->
```
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.25/k8sgpt_386.deb
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.28/k8sgpt_386.deb
sudo dpkg -i k8sgpt_386.deb
```
<!---x-release-please-end-->
@@ -67,7 +70,7 @@ brew install k8sgpt
<!---x-release-please-start-version-->
```
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.25/k8sgpt_amd64.deb
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.28/k8sgpt_amd64.deb
sudo dpkg -i k8sgpt_amd64.deb
```
<!---x-release-please-end-->
@@ -80,14 +83,14 @@ brew install k8sgpt
**32 bit:**
<!---x-release-please-start-version-->
```
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.25/k8sgpt_386.apk
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.28/k8sgpt_386.apk
apk add k8sgpt_386.apk
```
<!---x-release-please-end-->
**64 bit:**
<!---x-release-please-start-version-->
```
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.25/k8sgpt_amd64.apk
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.28/k8sgpt_amd64.apk
apk add k8sgpt_amd64.apk
```
<!---x-release-please-end-->x
@@ -125,14 +128,14 @@ _This mode of operation is ideal for continuous monitoring of your cluster and c
## Quick Start
* Currently the default AI provider is OpenAI, you will need to generate an API key from [OpenAI](https://openai.com)
* Currently, the default AI provider is OpenAI, you will need to generate an API key from [OpenAI](https://openai.com)
* You can do this by running `k8sgpt generate` to open a browser link to generate it
* Run `k8sgpt auth add` to set it in k8sgpt.
* You can provide the password directly using the `--password` flag.
* Run `k8sgpt filters` to manage the active filters used by the analyzer. By default, all filters are executed during analysis.
* Run `k8sgpt analyze` to run a scan.
* And use `k8sgpt analyze --explain` to get a more detailed explanation of the issues.
* You also run `k8sgpt analyze --with-doc` (with or without the explain flag) to get the official documentation from kubernetes.
* You also run `k8sgpt analyze --with-doc` (with or without the explain flag) to get the official documentation from Kubernetes.
## Analyzers
@@ -291,200 +294,30 @@ grpcurl -plaintext -d '{"namespace": "k8sgpt", "explain": false}' localhost:8080
```
</details>
## LLM AI Backends
## Key Features
K8sGPT uses the chosen LLM, generative AI provider when you want to explain the analysis results using --explain flag e.g. `k8sgpt analyze --explain`. You can use `--backend` flag to specify a configured provider (it's `openai` by default).
<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:
You can list available providers using `k8sgpt auth list`:
```
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:
Default:
> openai
Active:
Active:
Unused:
> openai
> azureopenai
Unused:
> localai
> noopai
> amazonbedrock
> azureopenai
> cohere
> amazonbedrock
> amazonsagemaker
> google
> huggingface
> noopai
> googlevertexai
```
For detailed documentation on how to configure and use each provider see [here](https://docs.k8sgpt.ai/reference/providers/backend/).
_To set a new default provider_
@@ -493,15 +326,12 @@ k8sgpt auth default -p azureopenai
Default provider set to azureopenai
```
</details>
## Key Features
<details>
With this option, the data is anonymized before being sent to the AI Backend. During the analysis execution, `k8sgpt` retrieves sensitive data (Kubernetes object names, labels, etc.). This data is masked when sent to the AI backend and replaced by a key that can be used to de-anonymize the data when the solution is returned to the user.
<summary> Anonymization </summary>
1. Error reported during analysis:
@@ -630,6 +460,33 @@ k8sgpt cache remove
```
</details>
<details>
<summary> Custom Analyzers</summary>
There may be scenarios where you wish to write your own analyzer in a language of your choice.
K8sGPT now supports the ability to do so by abiding by the [schema](https://github.com/k8sgpt-ai/schemas/blob/main/protobuf/schema/v1/analyzer.proto) and serving the analyzer for consumption.
To do so, define the analyzer within the K8sGPT configuration and it will add it into the scanning process.
In addition to this you will need to enable the following flag on analysis:
```
k8sgpt analyze --custom-analysis
```
Here is an example local host analyzer in [Rust](https://github.com/k8sgpt-ai/host-analyzer)
When this is run on `localhost:8080` the K8sGPT config can pick it up with the following additions:
```
custom_analyzers:
- name: host-analyzer
connection:
url: localhost
port: 8080
```
This now gives the ability to pass through hostOS information ( from this analyzer example ) to K8sGPT to use as context with normal analysis.
_See the docs on how to write a custom analyzer_
</details>
## Documentation

View File

@@ -16,23 +16,28 @@ package analyze
import (
"fmt"
"os"
"os/signal"
"syscall"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/ai/interactive"
"github.com/k8sgpt-ai/k8sgpt/pkg/analysis"
"github.com/spf13/cobra"
)
var (
explain bool
backend string
output string
filters []string
language string
nocache bool
namespace string
anonymize bool
maxConcurrency int
withDoc bool
explain bool
backend string
output string
filters []string
language string
nocache bool
namespace string
anonymize bool
maxConcurrency int
withDoc bool
interactiveMode bool
customAnalysis bool
)
// AnalyzeCmd represents the problems command
@@ -43,7 +48,6 @@ var AnalyzeCmd = &cobra.Command{
Long: `This command will find problems within your Kubernetes cluster and
provide you with a list of issues that need to be resolved`,
Run: func(cmd *cobra.Command, args []string) {
// Create analysis configuration first.
config, err := analysis.NewAnalysis(
backend,
@@ -54,13 +58,18 @@ var AnalyzeCmd = &cobra.Command{
explain,
maxConcurrency,
withDoc,
interactiveMode,
)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
defer config.Close()
if customAnalysis {
config.RunCustomAnalysis()
}
config.RunAnalysis()
if explain {
@@ -69,19 +78,42 @@ var AnalyzeCmd = &cobra.Command{
os.Exit(1)
}
}
// print results
output, err := config.PrintOutput(output)
output_data, err := config.PrintOutput(output)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
fmt.Println(string(output))
fmt.Println(string(output_data))
if interactiveMode && explain {
if output == "json" {
color.Yellow("Caution: interactive mode using --json enabled may use additional tokens.")
}
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
interactiveClient := interactive.NewInteractionRunner(config, output_data)
go interactiveClient.StartInteraction()
for {
select {
case res := <-sigs:
switch res {
default:
os.Exit(0)
}
case res := <-interactiveClient.State:
switch res {
case interactive.E_EXITED:
os.Exit(0)
}
}
}
}
},
}
func init() {
// namespace flag
AnalyzeCmd.Flags().StringVarP(&namespace, "namespace", "n", "", "Namespace to analyze")
// no cache flag
@@ -93,7 +125,7 @@ func init() {
// explain flag
AnalyzeCmd.Flags().BoolVarP(&explain, "explain", "e", false, "Explain the problem to me")
// add flag for backend
AnalyzeCmd.Flags().StringVarP(&backend, "backend", "b", "openai", "Backend AI provider")
AnalyzeCmd.Flags().StringVarP(&backend, "backend", "b", "", "Backend AI provider")
// output as json
AnalyzeCmd.Flags().StringVarP(&output, "output", "o", "text", "Output format (text, json)")
// add language options for output
@@ -102,4 +134,9 @@ func init() {
AnalyzeCmd.Flags().IntVarP(&maxConcurrency, "max-concurrency", "m", 10, "Maximum number of concurrent requests to the Kubernetes API server")
// kubernetes doc flag
AnalyzeCmd.Flags().BoolVarP(&withDoc, "with-doc", "d", false, "Give me the official documentation of the involved field")
// interactive mode flag
AnalyzeCmd.Flags().BoolVarP(&interactiveMode, "interactive", "i", false, "Enable interactive mode that allows further conversation with LLM about the problem. Works only with --explain flag")
// custom analysis flag
AnalyzeCmd.Flags().BoolVarP(&customAnalysis, "custom-analysis", "z", false, "Enable custom analyzers")
}

View File

@@ -119,6 +119,7 @@ var addCmd = &cobra.Command{
Engine: engine,
Temperature: temperature,
ProviderRegion: providerRegion,
ProviderId: providerId,
TopP: topP,
MaxTokens: maxTokens,
}
@@ -159,5 +160,7 @@ func init() {
// add flag for azure open ai engine/deployment name
addCmd.Flags().StringVarP(&engine, "engine", "e", "", "Azure AI deployment name (only for azureopenai backend)")
//add flag for amazonbedrock region name
addCmd.Flags().StringVarP(&providerRegion, "providerRegion", "r", "", "Provider Region name (only for amazonbedrock backend)")
addCmd.Flags().StringVarP(&providerRegion, "providerRegion", "r", "", "Provider Region name (only for amazonbedrock, googlevertexai backend)")
//add flag for vertexAI Project ID
addCmd.Flags().StringVarP(&providerId, "providerId", "i", "", "Provider specific ID for e.g. project (only for googlevertexai backend)")
}

View File

@@ -27,6 +27,7 @@ var (
engine string
temperature float32
providerRegion string
providerId string
topP float32
maxTokens int
)

3
cmd/cache/add.go vendored
View File

@@ -24,7 +24,8 @@ import (
)
var (
region string
region string
//nolint:unused
bucketName string
storageAccount string
containerName string

View File

@@ -15,15 +15,17 @@ package generate
import (
"fmt"
"os/exec"
"runtime"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"os/exec"
"runtime"
)
var (
backend string
backend string
backendType string
)
// generateCmd represents the auth command
@@ -33,7 +35,7 @@ var GenerateCmd = &cobra.Command{
Long: `Opens your browser to generate a key for your chosen backend.`,
Run: func(cmd *cobra.Command, args []string) {
backendType := viper.GetString("backend_type")
backendType = viper.GetString("backend_type")
if backendType == "" {
// Set the default backend
backend = "openai"
@@ -85,6 +87,6 @@ func printInstructions(isGui bool, backendType string) {
color.Green("Please open: https://beta.openai.com/account/api-keys to generate a key for %s", backendType)
fmt.Println("")
}
color.Green("Please copy the generated key and run `k8sgpt auth` to add it to your config file")
color.Green("Please copy the generated key and run `k8sgpt auth add` to add it to your config file")
fmt.Println("")
}

View File

@@ -21,9 +21,7 @@ import (
"github.com/spf13/viper"
)
var (
skipInstall bool
)
var skipInstall bool
// activateCmd represents the activate command
var activateCmd = &cobra.Command{
@@ -56,5 +54,4 @@ var activateCmd = &cobra.Command{
func init() {
IntegrationCmd.AddCommand(activateCmd)
activateCmd.Flags().BoolVarP(&skipInstall, "no-install", "s", false, "Only activate the integration filter without installing the filter (for example, if that filter plugin is already deployed in cluster, we do not need to re-install it again)")
}

View File

@@ -33,6 +33,7 @@ var (
port string
metricsPort string
backend string
enableHttp bool
)
var ServeCmd = &cobra.Command{
@@ -72,6 +73,7 @@ var ServeCmd = &cobra.Command{
model := os.Getenv("K8SGPT_MODEL")
baseURL := os.Getenv("K8SGPT_BASEURL")
engine := os.Getenv("K8SGPT_ENGINE")
proxyEndpoint := os.Getenv("K8SGPT_PROXY_ENDPOINT")
// If the envs are set, allocate in place to the aiProvider
// else exit with error
envIsSet := backend != "" || password != "" || model != ""
@@ -82,6 +84,7 @@ var ServeCmd = &cobra.Command{
Model: model,
BaseURL: baseURL,
Engine: engine,
ProxyEndpoint: proxyEndpoint,
Temperature: temperature(),
}
@@ -120,12 +123,18 @@ var ServeCmd = &cobra.Command{
color.Red("failed to create logger: %v", err)
os.Exit(1)
}
defer logger.Sync()
defer func() {
if err := logger.Sync(); err != nil {
color.Red("failed to sync logger: %v", err)
os.Exit(1)
}
}()
server := k8sgptserver.Config{
Backend: aiProvider.Name,
Port: port,
MetricsPort: metricsPort,
EnableHttp: enableHttp,
Token: aiProvider.Password,
Logger: logger,
}
@@ -153,4 +162,5 @@ func init() {
ServeCmd.Flags().StringVarP(&port, "port", "p", "8080", "Port to run the server on")
ServeCmd.Flags().StringVarP(&metricsPort, "metrics-port", "", "8081", "Port to run the metrics-server on")
ServeCmd.Flags().StringVarP(&backend, "backend", "b", "openai", "Backend AI provider")
ServeCmd.Flags().BoolVarP(&enableHttp, "http", "", false, "Enable REST/http using gppc-gateway")
}

View File

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

120
go.mod
View File

@@ -7,12 +7,12 @@ require (
github.com/fatih/color v1.16.0
github.com/magiconair/properties v1.8.7
github.com/mittwald/go-helm-client v0.12.5
github.com/sashabaranov/go-openai v1.17.10
github.com/schollz/progressbar/v3 v3.14.1
github.com/sashabaranov/go-openai v1.20.2
github.com/schollz/progressbar/v3 v3.14.2
github.com/spf13/cobra v1.8.0
github.com/spf13/viper v1.18.2
github.com/stretchr/testify v1.8.4
golang.org/x/term v0.16.0
github.com/stretchr/testify v1.9.0
golang.org/x/term v0.17.0
helm.sh/helm/v3 v3.13.3
k8s.io/api v0.28.4
k8s.io/apimachinery v0.28.4
@@ -24,59 +24,81 @@ require (
require github.com/adrg/xdg v0.4.0
require (
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.36.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.1
github.com/aws/aws-sdk-go v1.49.15
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc-ecosystem/gateway/v2 v2.19.1-20240213144542-6e830f3fdf19.1
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.3.0-20240213144542-6e830f3fdf19.2
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.32.0-20240213144542-6e830f3fdf19.1
cloud.google.com/go/storage v1.38.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.1
github.com/aws/aws-sdk-go v1.50.34
github.com/cohere-ai/cohere-go v0.2.0
github.com/google/generative-ai-go v0.5.0
github.com/google/generative-ai-go v0.8.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1
github.com/hupe1980/go-huggingface v0.0.15
github.com/olekukonko/tablewriter v0.0.5
google.golang.org/api v0.155.0
github.com/prometheus/prometheus v0.49.1
github.com/pterm/pterm v0.12.79
google.golang.org/api v0.167.0
gopkg.in/yaml.v2 v2.4.0
sigs.k8s.io/controller-runtime v0.16.3
sigs.k8s.io/gateway-api v1.0.0
)
require (
cloud.google.com/go v0.110.10 // indirect
atomicgo.dev/cursor v0.2.0 // indirect
atomicgo.dev/keyboard v0.2.9 // indirect
atomicgo.dev/schedule v0.1.0 // indirect
cloud.google.com/go v0.112.0 // indirect
cloud.google.com/go/ai v0.3.0 // indirect
cloud.google.com/go/compute v1.23.3 // indirect
cloud.google.com/go/aiplatform v1.59.0 // indirect
cloud.google.com/go/compute v1.24.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v1.1.5 // indirect
cloud.google.com/go/longrunning v0.5.4 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0 // indirect
cloud.google.com/go/iam v1.1.6 // indirect
cloud.google.com/go/longrunning v0.5.5 // indirect
cloud.google.com/go/vertexai v0.7.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 // indirect
github.com/Microsoft/hcsshim v0.11.4 // indirect
github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 // indirect
github.com/anchore/go-struct-converter v0.0.0-20230627203149-c72ef8859ca9 // indirect
github.com/cohere-ai/tokenizer v1.1.1 // indirect
github.com/containerd/console v1.0.3 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/distribution/reference v0.5.0 // indirect
github.com/dlclark/regexp2 v1.10.0 // indirect
github.com/evanphx/json-patch/v5 v5.7.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/golang-jwt/jwt/v5 v5.0.0 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/golang-jwt/jwt/v5 v5.2.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/googleapis/gax-go/v2 v2.12.1 // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jpillora/backoff v1.0.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/lithammer/fuzzysearch v1.1.8 // indirect
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/prometheus/common/sigv4 v0.1.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect
go.opentelemetry.io/otel/metric v1.21.0 // indirect
google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231211222908-989df2bf70f3 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 // indirect
go.opentelemetry.io/otel/metric v1.23.0 // indirect
google.golang.org/genproto v0.0.0-20240205150955-31a09d347014 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 // indirect
gopkg.in/evanphx/json-patch.v5 v5.7.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
require (
@@ -114,7 +136,7 @@ require (
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
github.com/go-logr/logr v1.3.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.20.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
@@ -128,7 +150,7 @@ require (
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.5.0 // indirect
github.com/google/uuid v1.6.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
@@ -141,7 +163,7 @@ require (
github.com/jmoiron/sqlx v1.3.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.2 // indirect
github.com/klauspost/compress v1.17.4 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/lib/pq v1.10.9 // indirect
@@ -170,11 +192,11 @@ require (
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.18.0
github.com/prometheus/client_golang v1.19.0
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/robfig/cron/v3 v3.0.1
github.com/rubenv/sql-migrate v1.5.2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
@@ -190,34 +212,32 @@ require (
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
go.opentelemetry.io/otel v1.21.0 // indirect
go.opentelemetry.io/otel/trace v1.21.0 // indirect
go.opentelemetry.io/otel v1.23.0 // indirect
go.opentelemetry.io/otel/trace v1.23.0 // indirect
go.starlark.net v0.0.0-20231016134836-22325403fcb3 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/oauth2 v0.15.0 // indirect
golang.org/x/sync v0.5.0 // indirect
golang.org/x/sys v0.16.0 // indirect
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.19.0 // indirect
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb // indirect
golang.org/x/net v0.21.0
golang.org/x/oauth2 v0.17.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/grpc v1.60.1
google.golang.org/protobuf v1.31.0 // indirect
google.golang.org/grpc v1.62.0
google.golang.org/protobuf v1.32.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.28.4
k8s.io/apiserver v0.28.4 // indirect
k8s.io/cli-runtime v0.28.4 // indirect
k8s.io/component-base v0.28.4 // indirect
k8s.io/klog/v2 v2.100.1 // indirect
k8s.io/klog/v2 v2.110.1 // indirect
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
k8s.io/utils v0.0.0-20240102154912-e7106e64919e
oras.land/oras-go v1.2.4 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/kustomize/api v0.15.0 // indirect

1157
go.sum

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

View File

@@ -10,6 +10,8 @@ import (
"github.com/aws/aws-sdk-go/service/bedrockruntime"
)
const amazonbedrockAIClientName = "amazonbedrock"
// AmazonBedRockClient represents the client for interacting with the Amazon Bedrock service.
type AmazonBedRockClient struct {
nopCloser
@@ -147,5 +149,5 @@ func (a *AmazonBedRockClient) GetCompletion(ctx context.Context, prompt string)
// GetName returns the name of the AmazonBedRockClient.
func (a *AmazonBedRockClient) GetName() string {
return "amazonbedrock"
return amazonbedrockAIClientName
}

View File

@@ -23,6 +23,8 @@ import (
"github.com/aws/aws-sdk-go/service/sagemakerruntime"
)
const amazonsagemakerAIClientName = "amazonsagemaker"
type SageMakerAIClient struct {
nopCloser
@@ -131,5 +133,5 @@ func (c *SageMakerAIClient) GetCompletion(_ context.Context, prompt string) (str
}
func (c *SageMakerAIClient) GetName() string {
return "amazonsagemaker"
return amazonsagemakerAIClientName
}

View File

@@ -3,10 +3,14 @@ package ai
import (
"context"
"errors"
"net/http"
"net/url"
"github.com/sashabaranov/go-openai"
)
const azureAIClientName = "azureopenai"
type AzureAIClient struct {
nopCloser
@@ -19,6 +23,7 @@ func (c *AzureAIClient) Configure(config IAIConfig) error {
token := config.GetPassword()
baseURL := config.GetBaseURL()
engine := config.GetEngine()
proxyEndpoint := config.GetProxyEndpoint()
defaultConfig := openai.DefaultAzureConfig(token, baseURL)
defaultConfig.AzureModelMapperFunc = func(model string) string {
@@ -29,6 +34,20 @@ func (c *AzureAIClient) Configure(config IAIConfig) error {
return azureModelMapping[model]
}
if proxyEndpoint != "" {
proxyUrl, err := url.Parse(proxyEndpoint)
if err != nil {
return err
}
transport := &http.Transport{
Proxy: http.ProxyURL(proxyUrl),
}
defaultConfig.HTTPClient = &http.Client{
Transport: transport,
}
}
client := openai.NewClientWithConfig(defaultConfig)
if client == nil {
return errors.New("error creating Azure OpenAI client")
@@ -58,5 +77,5 @@ func (c *AzureAIClient) GetCompletion(ctx context.Context, prompt string) (strin
}
func (c *AzureAIClient) GetName() string {
return "azureopenai"
return azureAIClientName
}

View File

@@ -20,9 +20,11 @@ import (
"github.com/cohere-ai/cohere-go"
)
const cohereAIClientName = "cohere"
type CohereClient struct {
nopCloser
client *cohere.Client
model string
temperature float32
@@ -68,5 +70,5 @@ func (c *CohereClient) GetCompletion(_ context.Context, prompt string) (string,
}
func (c *CohereClient) GetName() string {
return "cohere"
return cohereAIClientName
}

178
pkg/ai/googlevertexai.go Normal file
View File

@@ -0,0 +1,178 @@
/*
Copyright 2023 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ai
import (
"context"
"errors"
"fmt"
"cloud.google.com/go/vertexai/genai"
"github.com/fatih/color"
)
const googleVertexAIClientName = "googlevertexai"
type GoogleVertexAIClient struct {
client *genai.Client
model string
temperature float32
topP float32
maxTokens int
}
// Vertex AI Gemini supported Regions
// https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/gemini
const VERTEXAI_DEFAULT_REGION = "us-central1" // default use us-east-1 region
const (
US_Central_1 = "us-central1"
US_West_4 = "us-west4"
North_America_Northeast1 = "northamerica-northeast1"
US_East_4 = "us-east4"
US_West_1 = "us-west1"
Asia_Northeast_3 = "asia-northeast3"
Asia_Southeast_1 = "asia-southeast1"
Asia_Northeast_1 = "asia-northeast1"
)
var VERTEXAI_SUPPORTED_REGION = []string{
US_Central_1,
US_West_4,
North_America_Northeast1,
US_East_4,
US_West_1,
Asia_Northeast_3,
Asia_Southeast_1,
Asia_Northeast_1,
}
const (
ModelGeminiProV1 = "gemini-1.0-pro-001"
)
var VERTEXAI_MODELS = []string{
ModelGeminiProV1,
}
// GetModelOrDefault check config model
func GetVertexAIModelOrDefault(model string) string {
// Check if the provided model is in the list
for _, m := range VERTEXAI_MODELS {
if m == model {
return model // Return the provided model
}
}
// Return the default model if the provided model is not in the list
return VERTEXAI_MODELS[0]
}
// GetModelOrDefault check config region
func GetVertexAIRegionOrDefault(region string) string {
// Check if the provided model is in the list
for _, m := range VERTEXAI_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 VERTEXAI_DEFAULT_REGION
}
func (g *GoogleVertexAIClient) Configure(config IAIConfig) error {
ctx := context.Background()
// Currently you can access VertexAI either by being authenticated via OAuth or Bearer token so we need to consider both
projectId := config.GetProviderId()
region := GetVertexAIRegionOrDefault(config.GetProviderRegion())
client, err := genai.NewClient(ctx, projectId, region)
if err != nil {
return fmt.Errorf("creating genai Google SDK client: %w", err)
}
g.client = client
g.model = GetVertexAIModelOrDefault(config.GetModel())
g.temperature = config.GetTemperature()
g.topP = config.GetTopP()
g.maxTokens = config.GetMaxTokens()
return nil
}
func (g *GoogleVertexAIClient) GetCompletion(ctx context.Context, prompt string) (string, error) {
model := g.client.GenerativeModel(g.model)
model.SetTemperature(g.temperature)
model.SetTopP(g.topP)
model.SetMaxOutputTokens(int32(g.maxTokens))
// Google AI SDK is capable of different inputs than just text, for now set explicit text prompt type.
// Similarly, we could stream the response. For now k8sgpt does not support streaming.
resp, err := model.GenerateContent(ctx, genai.Text(prompt))
if err != nil {
return "", err
}
if len(resp.Candidates) == 0 {
if resp.PromptFeedback.BlockReason > 0 {
for _, r := range resp.PromptFeedback.SafetyRatings {
if !r.Blocked {
continue
}
return "", fmt.Errorf("complection blocked due to %v with probability %v", r.Category.String(), r.Probability.String())
}
}
return "", errors.New("no complection returned; unknown reason")
}
// Format output.
// TODO(bwplotka): Provider richer output in certain cases e.g. suddenly finished
// completion based on finish reasons or safety rankings.
got := resp.Candidates[0]
var output string
for _, part := range got.Content.Parts {
switch o := part.(type) {
case genai.Text:
output += string(o)
output += "\n"
default:
color.Yellow("found unsupported AI response part of type %T; ignoring", part)
}
}
if got.CitationMetadata != nil && len(got.CitationMetadata.Citations) > 0 {
output += "Citations:\n"
for _, source := range got.CitationMetadata.Citations {
// TODO(bwplotka): Give details around what exactly words could be attributed to the citation.
output += fmt.Sprintf("* %s, %s\n", source.URI, source.License)
}
}
return output, nil
}
func (g *GoogleVertexAIClient) GetName() string {
return googleVertexAIClientName
}
func (g *GoogleVertexAIClient) Close() {
if err := g.client.Close(); err != nil {
color.Red("googleai client close error: %v", err)
}
}

59
pkg/ai/huggingface.go Normal file
View File

@@ -0,0 +1,59 @@
package ai
import (
"context"
"github.com/hupe1980/go-huggingface"
"k8s.io/utils/ptr"
)
const huggingfaceAIClientName = "huggingface"
type HuggingfaceClient struct {
nopCloser
client *huggingface.InferenceClient
model string
topP float32
temperature float32
maxTokens int
}
func (c *HuggingfaceClient) Configure(config IAIConfig) error {
token := config.GetPassword()
client := huggingface.NewInferenceClient(token)
c.client = client
c.model = config.GetModel()
c.topP = config.GetTopP()
c.temperature = config.GetTemperature()
if config.GetMaxTokens() > 500 {
c.maxTokens = 500
} else {
c.maxTokens = config.GetMaxTokens()
}
return nil
}
func (c *HuggingfaceClient) GetCompletion(ctx context.Context, prompt string) (string, error) {
resp, err := c.client.Conversational(ctx, &huggingface.ConversationalRequest{
Inputs: huggingface.ConverstationalInputs{
Text: prompt,
},
Model: c.model,
Parameters: huggingface.ConversationalParameters{
TopP: ptr.To[float64](float64(c.topP)),
Temperature: ptr.To[float64](float64(c.temperature)),
MaxLength: &c.maxTokens,
},
Options: huggingface.Options{
WaitForModel: ptr.To[bool](true),
},
})
if err != nil {
return "", err
}
return resp.GeneratedText, nil
}
func (c *HuggingfaceClient) GetName() string { return huggingfaceAIClientName }

View File

@@ -27,16 +27,20 @@ var (
&AmazonBedRockClient{},
&SageMakerAIClient{},
&GoogleGenAIClient{},
&HuggingfaceClient{},
&GoogleVertexAIClient{},
}
Backends = []string{
"openai",
"localai",
"azureopenai",
"cohere",
"amazonbedrock",
"amazonsagemaker",
openAIClientName,
localAIClientName,
azureAIClientName,
cohereAIClientName,
amazonbedrockAIClientName,
amazonsagemakerAIClientName,
googleAIClientName,
"noopai",
noopAIClientName,
huggingfaceAIClientName,
googleVertexAIClientName,
}
)
@@ -62,12 +66,14 @@ type IAIConfig interface {
GetPassword() string
GetModel() string
GetBaseURL() string
GetProxyEndpoint() string
GetEndpointName() string
GetEngine() string
GetTemperature() float32
GetProviderRegion() string
GetTopP() float32
GetMaxTokens() int
GetProviderId() string
}
func NewClient(provider string) IAI {
@@ -90,10 +96,13 @@ type AIProvider struct {
Model string `mapstructure:"model"`
Password string `mapstructure:"password" yaml:"password,omitempty"`
BaseURL string `mapstructure:"baseurl" yaml:"baseurl,omitempty"`
ProxyEndpoint string `mapstructure:"proxyEndpoint" yaml:"proxyEndpoint,omitempty"`
ProxyPort string `mapstructure:"proxyPort" yaml:"proxyPort,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"`
ProviderId string `mapstructure:"providerid" yaml:"providerid,omitempty"`
TopP float32 `mapstructure:"topp" yaml:"topp,omitempty"`
MaxTokens int `mapstructure:"maxtokens" yaml:"maxtokens,omitempty"`
}
@@ -102,6 +111,10 @@ func (p *AIProvider) GetBaseURL() string {
return p.BaseURL
}
func (p *AIProvider) GetProxyEndpoint() string {
return p.ProxyEndpoint
}
func (p *AIProvider) GetEndpointName() string {
return p.EndpointName
}
@@ -133,7 +146,11 @@ func (p *AIProvider) GetProviderRegion() string {
return p.ProviderRegion
}
var passwordlessProviders = []string{"localai", "amazonsagemaker", "amazonbedrock"}
func (p *AIProvider) GetProviderId() string {
return p.ProviderId
}
var passwordlessProviders = []string{"localai", "amazonsagemaker", "amazonbedrock", "googlevertexai"}
func NeedPassword(backend string) bool {
for _, b := range passwordlessProviders {

View File

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

View File

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

View File

@@ -17,6 +17,8 @@ import (
"context"
)
const noopAIClientName = "noopai"
type NoOpAIClient struct {
nopCloser
}
@@ -31,5 +33,5 @@ func (c *NoOpAIClient) GetCompletion(_ context.Context, prompt string) (string,
}
func (c *NoOpAIClient) GetName() string {
return "noopai"
return noopAIClientName
}

View File

@@ -16,10 +16,14 @@ package ai
import (
"context"
"errors"
"net/http"
"net/url"
"github.com/sashabaranov/go-openai"
)
const openAIClientName = "openai"
type OpenAIClient struct {
nopCloser
@@ -39,12 +43,27 @@ const (
func (c *OpenAIClient) Configure(config IAIConfig) error {
token := config.GetPassword()
defaultConfig := openai.DefaultConfig(token)
proxyEndpoint := config.GetProxyEndpoint()
baseURL := config.GetBaseURL()
if baseURL != "" {
defaultConfig.BaseURL = baseURL
}
if proxyEndpoint != "" {
proxyUrl, err := url.Parse(proxyEndpoint)
if err != nil {
return err
}
transport := &http.Transport{
Proxy: http.ProxyURL(proxyUrl),
}
defaultConfig.HTTPClient = &http.Client{
Transport: transport,
}
}
client := openai.NewClientWithConfig(defaultConfig)
if client == nil {
return errors.New("error creating OpenAI client")
@@ -78,5 +97,5 @@ func (c *OpenAIClient) GetCompletion(ctx context.Context, prompt string) (string
}
func (c *OpenAIClient) GetName() string {
return "openai"
return openAIClientName
}

View File

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

View File

@@ -28,6 +28,7 @@ import (
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer"
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/custom"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
"github.com/schollz/progressbar/v3"
@@ -50,8 +51,10 @@ type Analysis struct {
WithDoc bool
}
type AnalysisStatus string
type AnalysisErrors []string
type (
AnalysisStatus string
AnalysisErrors []string
)
const (
StateOK AnalysisStatus = "OK"
@@ -75,6 +78,7 @@ func NewAnalysis(
explain bool,
maxConcurrency int,
withDoc bool,
interactiveMode bool,
) (*Analysis, error) {
// Get kubernetes client from viper.
kubecontext := viper.GetString("kubecontext")
@@ -120,11 +124,15 @@ func NewAnalysis(
}
// 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" {
// Hence, use the default provider only if the backend is not specified by the user.
if configAI.DefaultProvider != "" && backend == "" {
backend = configAI.DefaultProvider
}
if backend == "" {
backend = "openai"
}
var aiProvider ai.AIProvider
for _, provider := range configAI.Providers {
if backend == provider.Name {
@@ -146,6 +154,27 @@ func NewAnalysis(
return a, nil
}
func (a *Analysis) RunCustomAnalysis() {
var customAnalyzers []custom.CustomAnalyzer
if err := viper.UnmarshalKey("custom_analyzers", &customAnalyzers); err != nil {
a.Errors = append(a.Errors, err.Error())
}
for _, cAnalyzer := range customAnalyzers {
canClient, err := custom.NewClient(cAnalyzer.Connection)
if err != nil {
a.Errors = append(a.Errors, fmt.Sprintf("Client creation error for %s analyzer", cAnalyzer.Name))
continue
}
result, err := canClient.Run()
if err != nil {
a.Results = append(a.Results, result)
}
}
}
func (a *Analysis) RunAnalysis() {
activeFilters := viper.GetStringSlice("active_filters")

View File

@@ -37,7 +37,10 @@ func (GatewayAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
gtwList := &gtwapi.GatewayList{}
gc := &gtwapi.GatewayClass{}
client := a.Client.CtrlClient
gtwapi.AddToScheme(client.Scheme())
err := gtwapi.AddToScheme(client.Scheme())
if err != nil {
return nil, err
}
if err := client.List(a.Context, gtwList, &ctrl.ListOptions{}); err != nil {
return nil, err
}

View File

@@ -56,8 +56,15 @@ func TestGatewayAnalyzer(t *testing.T) {
Gateway := BuildGateway(ClassName, AcceptedStatus)
// Create a Gateway Analyzer instance with the fake client
scheme := scheme.Scheme
gtwapi.Install(scheme)
apiextensionsv1.AddToScheme(scheme)
err := gtwapi.Install(scheme)
if err != nil {
t.Error(err)
}
err = apiextensionsv1.AddToScheme(scheme)
if err != nil {
t.Error(err)
}
objects := []runtime.Object{
&Gateway,
&GatewayClass,
@@ -88,8 +95,14 @@ func TestMissingClassGatewayAnalyzer(t *testing.T) {
// Create a Gateway Analyzer instance with the fake client
scheme := scheme.Scheme
gtwapi.Install(scheme)
apiextensionsv1.AddToScheme(scheme)
err := gtwapi.Install(scheme)
if err != nil {
t.Error(err)
}
err = apiextensionsv1.AddToScheme(scheme)
if err != nil {
t.Error(err)
}
objects := []runtime.Object{
&Gateway,
}
@@ -121,8 +134,14 @@ func TestStatusGatewayAnalyzer(t *testing.T) {
// Create a Gateway Analyzer instance with the fake client
scheme := scheme.Scheme
gtwapi.Install(scheme)
apiextensionsv1.AddToScheme(scheme)
err := gtwapi.Install(scheme)
if err != nil {
t.Error(err)
}
err = apiextensionsv1.AddToScheme(scheme)
if err != nil {
t.Error(err)
}
objects := []runtime.Object{
&Gateway,
&GatewayClass,

View File

@@ -35,7 +35,10 @@ func (GatewayClassAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error)
gcList := &gtwapi.GatewayClassList{}
client := a.Client.CtrlClient
gtwapi.AddToScheme(client.Scheme())
err := gtwapi.AddToScheme(client.Scheme())
if err != nil {
return nil, err
}
if err := client.List(a.Context, gcList, &ctrl.ListOptions{}); err != nil {
return nil, err
}

View File

@@ -29,8 +29,14 @@ func TestGatewayClassAnalyzer(t *testing.T) {
GatewayClass.Status.Conditions = []metav1.Condition{BadCondition}
// Create a GatewayClassAnalyzer instance with the fake client
scheme := scheme.Scheme
gtwapi.Install(scheme)
apiextensionsv1.AddToScheme(scheme)
err := gtwapi.Install(scheme)
if err != nil {
t.Error(err)
}
err = apiextensionsv1.AddToScheme(scheme)
if err != nil {
t.Error(err)
}
fakeClient := fakeclient.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(GatewayClass).Build()

View File

@@ -38,14 +38,16 @@ func (HTTPRouteAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
gtw := &gtwapi.Gateway{}
service := &corev1.Service{}
client := a.Client.CtrlClient
gtwapi.AddToScheme(client.Scheme())
err := gtwapi.AddToScheme(client.Scheme())
if err != nil {
return nil, err
}
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

View File

@@ -104,8 +104,14 @@ func TestGWMissiningHTTRouteAnalyzer(t *testing.T) {
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)
err := gtwapi.Install(scheme)
if err != nil {
t.Error(err)
}
err = apiextensionsv1.AddToScheme(scheme)
if err != nil {
t.Error(err)
}
objects := []runtime.Object{
&HTTPRoute,
}
@@ -156,8 +162,14 @@ func TestGWConfigSameHTTRouteAnalyzer(t *testing.T) {
Gateway := BuildRouteGateway("differentnamespace", "gatewayname", "Same")
// Create a Gateway Analyzer instance with the fake client
scheme := scheme.Scheme
gtwapi.Install(scheme)
apiextensionsv1.AddToScheme(scheme)
err := gtwapi.Install(scheme)
if err != nil {
t.Error(err)
}
err = apiextensionsv1.AddToScheme(scheme)
if err != nil {
t.Error(err)
}
objects := []runtime.Object{
&HTTPRoute,
&Gateway,
@@ -207,8 +219,14 @@ func TestGWConfigSelectorHTTRouteAnalyzer(t *testing.T) {
Gateway := BuildRouteGateway("default", "gatewayname", "Selector")
// Create a Gateway Analyzer instance with the fake client
scheme := scheme.Scheme
gtwapi.Install(scheme)
apiextensionsv1.AddToScheme(scheme)
err := gtwapi.Install(scheme)
if err != nil {
t.Error(err)
}
err = apiextensionsv1.AddToScheme(scheme)
if err != nil {
t.Error(err)
}
objects := []runtime.Object{
&HTTPRoute,
&Gateway,
@@ -259,8 +277,14 @@ func TestSvcMissingHTTRouteAnalyzer(t *testing.T) {
Gateway := BuildRouteGateway("default", "gatewayname", "Same")
// Create a Gateway Analyzer instance with the fake client
scheme := scheme.Scheme
gtwapi.Install(scheme)
apiextensionsv1.AddToScheme(scheme)
err := gtwapi.Install(scheme)
if err != nil {
t.Error(err)
}
err = apiextensionsv1.AddToScheme(scheme)
if err != nil {
t.Error(err)
}
objects := []runtime.Object{
&HTTPRoute,
&Gateway,
@@ -332,8 +356,14 @@ func TestSvcDifferentPortHTTRouteAnalyzer(t *testing.T) {
Gateway := BuildRouteGateway("default", "gatewayname", "Same")
// Create a Gateway Analyzer instance with the fake client
scheme := scheme.Scheme
gtwapi.Install(scheme)
apiextensionsv1.AddToScheme(scheme)
err := gtwapi.Install(scheme)
if err != nil {
t.Error(err)
}
err = apiextensionsv1.AddToScheme(scheme)
if err != nil {
t.Error(err)
}
objects := []runtime.Object{
&HTTPRoute,
&Gateway,

View File

@@ -49,29 +49,17 @@ func (LogAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
// Iterate through each pod
for _, pod := range list.Items {
var failures []common.Failure
podName := pod.Name
podLogOptions := v1.PodLogOptions{
TailLines: &tailLines,
}
podLogs, err := a.Client.Client.CoreV1().Pods(pod.Namespace).GetLogs(podName, &podLogOptions).DoRaw(a.Context)
if err != nil {
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Error %s from Pod %s", err.Error(), pod.Name),
Sensitive: []common.Sensitive{
{
Unmasked: pod.Name,
Masked: util.MaskString(pod.Name),
},
},
})
} else {
rawlogs := string(podLogs)
if errorPattern.MatchString(strings.ToLower(rawlogs)) {
for _, c := range pod.Spec.Containers {
var failures []common.Failure
podLogOptions := v1.PodLogOptions{
TailLines: &tailLines,
Container: c.Name,
}
podLogs, err := a.Client.Client.CoreV1().Pods(pod.Namespace).GetLogs(podName, &podLogOptions).DoRaw(a.Context)
if err != nil {
failures = append(failures, common.Failure{
Text: printErrorLines(pod.Name, pod.Namespace, rawlogs, errorPattern),
Text: fmt.Sprintf("Error %s from Pod %s", err.Error(), pod.Name),
Sensitive: []common.Sensitive{
{
Unmasked: pod.Name,
@@ -79,14 +67,27 @@ func (LogAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
},
},
})
} else {
rawlogs := string(podLogs)
if errorPattern.MatchString(strings.ToLower(rawlogs)) {
failures = append(failures, common.Failure{
Text: printErrorLines(pod.Name, pod.Namespace, rawlogs, errorPattern),
Sensitive: []common.Sensitive{
{
Unmasked: pod.Name,
Masked: util.MaskString(pod.Name),
},
},
})
}
}
}
if len(failures) > 0 {
preAnalysis[fmt.Sprintf("%s/%s", pod.Namespace, pod.Name)] = common.PreAnalysis{
FailureDetails: failures,
Pod: pod,
if len(failures) > 0 {
preAnalysis[fmt.Sprintf("%s/%s/%s", pod.Namespace, pod.Name, c.Name)] = common.PreAnalysis{
FailureDetails: failures,
Pod: pod,
}
AnalyzerErrorsMetric.WithLabelValues(kind, pod.Name, pod.Namespace).Set(float64(len(failures)))
}
AnalyzerErrorsMetric.WithLabelValues(kind, pod.Name, pod.Namespace).Set(float64(len(failures)))
}
}
for key, value := range preAnalysis {

View File

@@ -75,6 +75,11 @@ func (MutatingWebhookAnalyzer) Analyze(a common.Analyzer) ([]common.Result, erro
},
},
})
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)))
continue
}

View File

@@ -0,0 +1,140 @@
/*
Copyright 2024 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package analyzer
import (
"context"
"testing"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/stretchr/testify/require"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
)
func TestMutatingWebhookAnalyzer(t *testing.T) {
config := common.Analyzer{
Client: &kubernetes.Client{
Client: fake.NewSimpleClientset(
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "Pod1",
Namespace: "default",
Labels: map[string]string{
"pod": "Pod1",
},
},
},
&v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "test-service1",
Namespace: "default",
},
Spec: v1.ServiceSpec{
Selector: map[string]string{
"pod": "Pod1",
},
},
},
&v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "test-service2",
Namespace: "test",
},
Spec: v1.ServiceSpec{
// No such pod exists in the test namespace
Selector: map[string]string{
"pod": "Pod2",
},
},
},
&v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "test-service3",
Namespace: "test",
},
Spec: v1.ServiceSpec{
// len(service.Spec.Selector) == 0
Selector: map[string]string{},
},
},
&admissionregistrationv1.MutatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: "test-mutating-webhook-config",
Namespace: "test",
},
Webhooks: []admissionregistrationv1.MutatingWebhook{
{
// Failure: Pointing to an inactive receiver pod
Name: "webhook1",
ClientConfig: admissionregistrationv1.WebhookClientConfig{
Service: &admissionregistrationv1.ServiceReference{
Name: "test-service1",
Namespace: "default",
},
},
},
{
// Failure: No active pods found in the test namespace
Name: "webhook2",
ClientConfig: admissionregistrationv1.WebhookClientConfig{
Service: &admissionregistrationv1.ServiceReference{
Name: "test-service2",
Namespace: "test",
},
},
},
{
Name: "webhook3",
ClientConfig: admissionregistrationv1.WebhookClientConfig{
Service: &admissionregistrationv1.ServiceReference{
Name: "test-service3",
Namespace: "test",
},
},
},
{
// Failure: Service doesn't exist.
Name: "webhook4",
ClientConfig: admissionregistrationv1.WebhookClientConfig{
Service: &admissionregistrationv1.ServiceReference{
Name: "test-service4-doesn't-exist",
Namespace: "test",
},
},
},
{
// Service is nil.
Name: "webhook5",
ClientConfig: admissionregistrationv1.WebhookClientConfig{},
},
},
},
),
},
Context: context.Background(),
Namespace: "default",
}
mwAnalyzer := MutatingWebhookAnalyzer{}
results, err := mwAnalyzer.Analyze(config)
require.NoError(t, err)
// The results should contain: webhook1, webhook2, and webhook4
resultsLen := 3
require.Equal(t, resultsLen, len(results))
}

View File

@@ -50,6 +50,11 @@ func (PdbAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
for _, pdb := range list.Items {
var failures []common.Failure
// Before accessing the Conditions, check if they exist or not.
if len(pdb.Status.Conditions) == 0 {
continue
}
if pdb.Status.Conditions[0].Type == "DisruptionAllowed" && pdb.Status.Conditions[0].Status == "False" {
var doc string
if pdb.Spec.MaxUnavailable != nil {

122
pkg/analyzer/pdb_test.go Normal file
View File

@@ -0,0 +1,122 @@
/*
Copyright 2024 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package analyzer
import (
"context"
"testing"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/stretchr/testify/require"
policyv1 "k8s.io/api/policy/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/kubernetes/fake"
)
func TestPodDisruptionBudgetAnalyzer(t *testing.T) {
config := common.Analyzer{
Client: &kubernetes.Client{
Client: fake.NewSimpleClientset(
&policyv1.PodDisruptionBudget{
ObjectMeta: metav1.ObjectMeta{
Name: "PDB1",
Namespace: "test",
},
// Status conditions are nil.
Status: policyv1.PodDisruptionBudgetStatus{
Conditions: nil,
},
},
&policyv1.PodDisruptionBudget{
ObjectMeta: metav1.ObjectMeta{
Name: "PDB2",
Namespace: "test",
},
// Status conditions are empty.
Status: policyv1.PodDisruptionBudgetStatus{
Conditions: []metav1.Condition{},
},
},
&policyv1.PodDisruptionBudget{
ObjectMeta: metav1.ObjectMeta{
Name: "PDB3",
Namespace: "test",
},
Status: policyv1.PodDisruptionBudgetStatus{
Conditions: []metav1.Condition{
{
Type: "DisruptionAllowed",
Status: "False",
Reason: "test reason",
},
},
},
Spec: policyv1.PodDisruptionBudgetSpec{
MaxUnavailable: &intstr.IntOrString{
Type: 0,
IntVal: 17,
StrVal: "17",
},
MinAvailable: &intstr.IntOrString{
Type: 0,
IntVal: 7,
StrVal: "7",
},
// MatchLabels specified.
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"label1": "test1",
"label2": "test2",
},
},
},
},
&policyv1.PodDisruptionBudget{
ObjectMeta: metav1.ObjectMeta{
Name: "PDB4",
Namespace: "test",
},
Status: policyv1.PodDisruptionBudgetStatus{
Conditions: []metav1.Condition{
{
Type: "DisruptionAllowed",
Status: "False",
Reason: "test reason",
},
},
},
// Match Labels Empty.
Spec: policyv1.PodDisruptionBudgetSpec{
Selector: &metav1.LabelSelector{},
},
},
),
},
Context: context.Background(),
Namespace: "test",
}
pdbAnalyzer := PdbAnalyzer{}
results, err := pdbAnalyzer.Analyze(config)
require.NoError(t, err)
for _, result := range results {
require.Equal(t, "test/PDB3", result.Name)
for _, failure := range result.Error {
require.Contains(t, failure.Text, "expected pdb pod label")
}
}
}

View File

@@ -77,7 +77,7 @@ func (PodAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
if err != nil || evt == nil {
continue
}
if evt.Reason == "FailedCreatePodSandBox" && evt.Message != "" {
if isEvtErrorReason(evt.Reason) && evt.Message != "" {
failures = append(failures, common.Failure{
Text: evt.Message,
Sensitive: []common.Sensitive{},
@@ -137,7 +137,21 @@ func (PodAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
func isErrorReason(reason string) bool {
failureReasons := []string{
"CrashLoopBackOff", "ImagePullBackOff", "CreateContainerConfigError", "PreCreateHookError", "CreateContainerError", "PreStartHookError", "RunContainerError", "ImageInspectError", "ErrImagePull", "ErrImageNeverPull", "InvalidImageName",
"CrashLoopBackOff", "ImagePullBackOff", "CreateContainerConfigError", "PreCreateHookError", "CreateContainerError",
"PreStartHookError", "RunContainerError", "ImageInspectError", "ErrImagePull", "ErrImageNeverPull", "InvalidImageName",
}
for _, r := range failureReasons {
if r == reason {
return true
}
}
return false
}
func isEvtErrorReason(reason string) bool {
failureReasons := []string{
"FailedCreatePodSandBox", "FailedMount",
}
for _, r := range failureReasons {

View File

@@ -18,6 +18,7 @@ import (
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
appsv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@@ -43,7 +44,7 @@ func (PvcAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
var failures []common.Failure
// Check for empty rs
if pvc.Status.Phase == "Pending" {
if pvc.Status.Phase == appsv1.ClaimPending {
// parse the event log and append details
evt, err := FetchLatestEvent(a.Context, a.Client, pvc.Namespace, pvc.Name)

224
pkg/analyzer/pvc_test.go Normal file
View File

@@ -0,0 +1,224 @@
/*
Copyright 2024 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package analyzer
import (
"context"
"sort"
"testing"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/stretchr/testify/require"
appsv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
)
func TestPersistentVolumeClaimAnalyzer(t *testing.T) {
tests := []struct {
name string
config common.Analyzer
expectations []string
}{
{
name: "PV1 and PVC5 report failures",
config: common.Analyzer{
Client: &kubernetes.Client{
Client: fake.NewSimpleClientset(
&appsv1.Event{
// This is the latest event.
ObjectMeta: metav1.ObjectMeta{
Name: "Event1",
Namespace: "default",
},
Reason: "ProvisioningFailed",
Message: "PVC provisioning failed",
},
&appsv1.Event{
ObjectMeta: metav1.ObjectMeta{
// This event won't get selected.
Name: "Event2",
Namespace: "test",
},
},
&appsv1.Event{
ObjectMeta: metav1.ObjectMeta{
Name: "Event3",
Namespace: "default",
},
},
&appsv1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "PVC1",
Namespace: "default",
},
Status: appsv1.PersistentVolumeClaimStatus{
Phase: appsv1.ClaimPending,
},
},
&appsv1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "PVC2",
Namespace: "default",
},
Status: appsv1.PersistentVolumeClaimStatus{
// Won't contribute to failures.
Phase: appsv1.ClaimBound,
},
},
&appsv1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "PVC3",
Namespace: "default",
},
Status: appsv1.PersistentVolumeClaimStatus{
// Won't contribute to failures.
Phase: appsv1.ClaimLost,
},
},
&appsv1.PersistentVolumeClaim{
// PVCs in namespace other than "default" won't be discovered.
ObjectMeta: metav1.ObjectMeta{
Name: "PVC4",
Namespace: "test",
},
Status: appsv1.PersistentVolumeClaimStatus{
Phase: appsv1.ClaimLost,
},
},
&appsv1.PersistentVolumeClaim{
// PVCs in namespace other than "default" won't be discovered.
ObjectMeta: metav1.ObjectMeta{
Name: "PVC5",
Namespace: "default",
},
Status: appsv1.PersistentVolumeClaimStatus{
Phase: appsv1.ClaimPending,
},
},
),
},
Context: context.Background(),
Namespace: "default",
},
expectations: []string{
"default/PVC1",
"default/PVC5",
},
},
{
name: "no event",
config: common.Analyzer{
Client: &kubernetes.Client{
Client: fake.NewSimpleClientset(
&appsv1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "PVC1",
Namespace: "default",
},
Status: appsv1.PersistentVolumeClaimStatus{
Phase: appsv1.ClaimPending,
},
},
),
},
Context: context.Background(),
Namespace: "default",
},
},
{
name: "event other than provision failure",
config: common.Analyzer{
Client: &kubernetes.Client{
Client: fake.NewSimpleClientset(
&appsv1.Event{
ObjectMeta: metav1.ObjectMeta{
Name: "Event1",
Namespace: "default",
},
// Any reason other than ProvisioningFailed won't result in failure.
Reason: "UnknownReason",
},
&appsv1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "PVC1",
Namespace: "default",
},
Status: appsv1.PersistentVolumeClaimStatus{
Phase: appsv1.ClaimPending,
},
},
),
},
Context: context.Background(),
Namespace: "default",
},
},
{
name: "event without error message",
config: common.Analyzer{
Client: &kubernetes.Client{
Client: fake.NewSimpleClientset(
&appsv1.Event{
ObjectMeta: metav1.ObjectMeta{
Name: "Event1",
Namespace: "default",
},
// Event without any error message won't result in failure.
Reason: "ProvisioningFailed",
},
&appsv1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "PVC1",
Namespace: "default",
},
Status: appsv1.PersistentVolumeClaimStatus{
Phase: appsv1.ClaimPending,
},
},
),
},
Context: context.Background(),
Namespace: "default",
},
},
}
pvcAnalyzer := PvcAnalyzer{}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
results, err := pvcAnalyzer.Analyze(tt.config)
require.NoError(t, err)
if tt.expectations == nil {
require.Equal(t, 0, len(results))
} else {
sort.Slice(results, func(i, j int) bool {
return results[i].Name < results[j].Name
})
require.Equal(t, len(tt.expectations), len(results))
for i, expectation := range tt.expectations {
require.Equal(t, expectation, results[i].Name)
for _, failure := range results[i].Error {
require.Equal(t, "PVC provisioning failed", failure.Text)
}
}
}
})
}
}

153
pkg/analyzer/rs_test.go Normal file
View File

@@ -0,0 +1,153 @@
/*
Copyright 2024 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package analyzer
import (
"context"
"sort"
"testing"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/stretchr/testify/require"
appsv1 "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
)
func TestReplicaSetAnalyzer(t *testing.T) {
config := common.Analyzer{
Client: &kubernetes.Client{
Client: fake.NewSimpleClientset(
&appsv1.ReplicaSet{
ObjectMeta: metav1.ObjectMeta{
Name: "ReplicaSet1",
Namespace: "default",
},
Status: appsv1.ReplicaSetStatus{
Replicas: 0,
Conditions: []appsv1.ReplicaSetCondition{
{
// Should contribute to failures.
Type: appsv1.ReplicaSetReplicaFailure,
Reason: "FailedCreate",
Message: "failed to create test replica set 1",
},
},
},
},
&appsv1.ReplicaSet{
// This replicaset won't be discovered as it is not in the
// default namespace.
ObjectMeta: metav1.ObjectMeta{
Name: "ReplicaSet2",
Namespace: "test",
},
},
&appsv1.ReplicaSet{
ObjectMeta: metav1.ObjectMeta{
Name: "ReplicaSet3",
Namespace: "default",
},
Status: appsv1.ReplicaSetStatus{
Replicas: 0,
Conditions: []appsv1.ReplicaSetCondition{
{
Type: appsv1.ReplicaSetReplicaFailure,
// Should not be included in the failures.
Reason: "RandomError",
},
},
},
},
&appsv1.ReplicaSet{
ObjectMeta: metav1.ObjectMeta{
Name: "ReplicaSet4",
Namespace: "default",
},
Status: appsv1.ReplicaSetStatus{
Replicas: 0,
Conditions: []appsv1.ReplicaSetCondition{
{
// Should contribute to failures.
Type: appsv1.ReplicaSetReplicaFailure,
Reason: "FailedCreate",
Message: "failed to create test replica set 4 condition 1",
},
{
// Should not contribute to failures.
Type: appsv1.ReplicaSetReplicaFailure,
Reason: "Unknown",
},
{
// Should not contribute to failures.
Type: appsv1.ReplicaSetReplicaFailure,
Reason: "FailedCreate",
Message: "failed to create test replica set 4 condition 3",
},
},
},
},
&appsv1.ReplicaSet{
// Replicaset without any failures.
ObjectMeta: metav1.ObjectMeta{
Name: "ReplicaSet5",
Namespace: "default",
},
Status: appsv1.ReplicaSetStatus{
Replicas: 3,
},
},
),
},
Context: context.Background(),
Namespace: "default",
}
rsAnalyzer := ReplicaSetAnalyzer{}
results, err := rsAnalyzer.Analyze(config)
require.NoError(t, err)
sort.Slice(results, func(i, j int) bool {
return results[i].Name < results[j].Name
})
expectations := []struct {
name string
failuresText []string
}{
{
name: "default/ReplicaSet1",
failuresText: []string{
"failed to create test replica set 1",
},
},
{
name: "default/ReplicaSet4",
failuresText: []string{
"failed to create test replica set 4 condition 1",
"failed to create test replica set 4 condition 3",
},
},
}
require.Equal(t, len(expectations), len(results))
for i, expectation := range expectations {
require.Equal(t, expectation.name, results[i].Name)
for j, failure := range results[i].Error {
require.Equal(t, expectation.failuresText[j], failure.Text)
}
}
}

View File

@@ -98,16 +98,18 @@ func (ServiceAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
count++
pods = append(pods, addresses.TargetRef.Kind+"/"+addresses.TargetRef.Name)
}
doc := apiDoc.GetApiDocV2("subsets.notReadyAddresses")
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Service has not ready endpoints, pods: %s, expected %d", pods, count),
KubernetesDoc: doc,
Sensitive: []common.Sensitive{},
})
}
}
if count > 0 {
doc := apiDoc.GetApiDocV2("subsets.notReadyAddresses")
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Service has not ready endpoints, pods: %s, expected %d", pods, count),
KubernetesDoc: doc,
Sensitive: []common.Sensitive{},
})
}
}
if len(failures) > 0 {

View File

@@ -15,108 +15,160 @@ package analyzer
import (
"context"
"sort"
"testing"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/magiconair/properties/assert"
"github.com/stretchr/testify/require"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/tools/leaderelection/resourcelock"
)
func TestServiceAnalyzer(t *testing.T) {
clientset := fake.NewSimpleClientset(&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",
},
}})
config := common.Analyzer{
Client: &kubernetes.Client{
Client: clientset,
Client: fake.NewSimpleClientset(
&v1.Endpoints{
ObjectMeta: metav1.ObjectMeta{
Name: "Endpoint1",
Namespace: "test",
},
// Endpoint with non-zero subsets.
Subsets: []v1.EndpointSubset{
{
// These not ready end points will contribute to failures.
NotReadyAddresses: []v1.EndpointAddress{
{
TargetRef: &v1.ObjectReference{
Kind: "test-reference",
Name: "reference1",
},
},
{
TargetRef: &v1.ObjectReference{
Kind: "test-reference",
Name: "reference2",
},
},
},
},
{
// These not ready end points will contribute to failures.
NotReadyAddresses: []v1.EndpointAddress{
{
TargetRef: &v1.ObjectReference{
Kind: "test-reference",
Name: "reference3",
},
},
},
},
},
},
&v1.Endpoints{
ObjectMeta: metav1.ObjectMeta{
Name: "Endpoint2",
Namespace: "test",
Annotations: map[string]string{
// Leader election record annotation key defined.
resourcelock.LeaderElectionRecordAnnotationKey: "this is okay",
},
},
// Endpoint with zero subsets.
},
&v1.Endpoints{
ObjectMeta: metav1.ObjectMeta{
// This won't contribute to any failures.
Name: "non-existent-service",
Namespace: "test",
Annotations: map[string]string{},
},
// Endpoint with zero subsets.
},
&v1.Endpoints{
ObjectMeta: metav1.ObjectMeta{
Name: "Service1",
Namespace: "test",
Annotations: map[string]string{},
},
// Endpoint with zero subsets.
},
&v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "Service1",
Namespace: "test",
},
Spec: v1.ServiceSpec{
Selector: map[string]string{
"app1": "test-app1",
"app2": "test-app2",
},
},
},
&v1.Service{
ObjectMeta: metav1.ObjectMeta{
// This service won't be discovered.
Name: "Service2",
Namespace: "default",
},
Spec: v1.ServiceSpec{
Selector: map[string]string{
"app1": "test-app1",
"app2": "test-app2",
},
},
},
&v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "Service3",
Namespace: "test",
},
Spec: v1.ServiceSpec{
// No Spec Selector
},
},
),
},
Context: context.Background(),
Namespace: "default",
Namespace: "test",
}
serviceAnalyzer := ServiceAnalyzer{}
analysisResults, err := serviceAnalyzer.Analyze(config)
if err != nil {
t.Error(err)
sAnalyzer := ServiceAnalyzer{}
results, err := sAnalyzer.Analyze(config)
require.NoError(t, err)
sort.Slice(results, func(i, j int) bool {
return results[i].Name < results[j].Name
})
expectations := []struct {
name string
failuresText []string
}{
{
name: "test/Endpoint1",
failuresText: []string{
"Service has not ready endpoints, pods",
},
},
{
name: "test/Service1",
failuresText: []string{
"Service has no endpoints, expected label",
"Service has no endpoints, expected label",
},
},
}
require.Equal(t, len(expectations), len(results))
for i, result := range results {
require.Equal(t, expectations[i].name, result.Name)
for j, failure := range result.Error {
require.Contains(t, failure.Text, expectations[i].failuresText[j])
}
}
assert.Equal(t, len(analysisResults), 1)
}
func TestServiceAnalyzerNamespaceFiltering(t *testing.T) {
clientset := fake.NewSimpleClientset(
&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",
},
},
},
&v1.Endpoints{
ObjectMeta: metav1.ObjectMeta{
Name: "example",
Namespace: "other-namespace",
Annotations: map[string]string{},
},
},
&v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "example",
Namespace: "other-namespace",
Annotations: map[string]string{},
},
Spec: v1.ServiceSpec{
Selector: map[string]string{
"app": "example",
},
},
},
)
config := common.Analyzer{
Client: &kubernetes.Client{
Client: clientset,
},
Context: context.Background(),
Namespace: "default",
}
serviceAnalyzer := ServiceAnalyzer{}
analysisResults, err := serviceAnalyzer.Analyze(config)
if err != nil {
t.Error(err)
}
assert.Equal(t, len(analysisResults), 1)
}

View File

@@ -73,6 +73,11 @@ func (ValidatingWebhookAnalyzer) Analyze(a common.Analyzer) ([]common.Result, er
},
},
})
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)))
continue
}

View File

@@ -0,0 +1,140 @@
/*
Copyright 2024 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package analyzer
import (
"context"
"testing"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/stretchr/testify/require"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
)
func TestValidatingWebhookAnalyzer(t *testing.T) {
config := common.Analyzer{
Client: &kubernetes.Client{
Client: fake.NewSimpleClientset(
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "Pod1",
Namespace: "default",
Labels: map[string]string{
"pod": "Pod1",
},
},
},
&v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "test-service1",
Namespace: "default",
},
Spec: v1.ServiceSpec{
Selector: map[string]string{
"pod": "Pod1",
},
},
},
&v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "test-service2",
Namespace: "test",
},
Spec: v1.ServiceSpec{
// No such pod exists in the test namespace
Selector: map[string]string{
"pod": "Pod2",
},
},
},
&v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "test-service3",
Namespace: "test",
},
Spec: v1.ServiceSpec{
// len(service.Spec.Selector) == 0
Selector: map[string]string{},
},
},
&admissionregistrationv1.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: "test-validating-webhook-config",
Namespace: "test",
},
Webhooks: []admissionregistrationv1.ValidatingWebhook{
{
// Failure: Pointing to an inactive receiver pod
Name: "webhook1",
ClientConfig: admissionregistrationv1.WebhookClientConfig{
Service: &admissionregistrationv1.ServiceReference{
Name: "test-service1",
Namespace: "default",
},
},
},
{
// Failure: No active pods found in the test namespace
Name: "webhook2",
ClientConfig: admissionregistrationv1.WebhookClientConfig{
Service: &admissionregistrationv1.ServiceReference{
Name: "test-service2",
Namespace: "test",
},
},
},
{
Name: "webhook3",
ClientConfig: admissionregistrationv1.WebhookClientConfig{
Service: &admissionregistrationv1.ServiceReference{
Name: "test-service3",
Namespace: "test",
},
},
},
{
// Failure: Service doesn't exist.
Name: "webhook4",
ClientConfig: admissionregistrationv1.WebhookClientConfig{
Service: &admissionregistrationv1.ServiceReference{
Name: "test-service4-doesn't-exist",
Namespace: "test",
},
},
},
{
// Service is nil.
Name: "webhook5",
ClientConfig: admissionregistrationv1.WebhookClientConfig{},
},
},
},
),
},
Context: context.Background(),
Namespace: "default",
}
vwAnalyzer := ValidatingWebhookAnalyzer{}
results, err := vwAnalyzer.Analyze(config)
require.NoError(t, err)
// The results should contain: webhook1, webhook2, and webhook4
resultsLen := 3
require.Equal(t, resultsLen, len(results))
}

57
pkg/custom/client.go Normal file
View File

@@ -0,0 +1,57 @@
package custom
import (
rpc "buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go/schema/v1/schemav1grpc"
schemav1 "buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go/schema/v1"
"context"
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
type Client struct {
c *grpc.ClientConn
analyzerClient rpc.AnalyzerServiceClient
}
func NewClient(c Connection) (*Client, error) {
conn, err := grpc.Dial(fmt.Sprintf("%s:%s", c.Url, c.Port), grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return nil, err
}
client := rpc.NewAnalyzerServiceClient(conn)
return &Client{
c: conn,
analyzerClient: client,
}, nil
}
func (cli *Client) Run() (common.Result, error) {
var result common.Result
req := &schemav1.AnalyzerRunRequest{}
res, err := cli.analyzerClient.Run(context.Background(), req)
if err != nil {
return result, err
}
if res.Result != nil {
// We should refactor this, because Error and Failure do not map 1:1 from K8sGPT/schema
var errorsFound []common.Failure
for _, e := range res.Result.Error {
errorsFound = append(errorsFound, common.Failure{
Text: e.Text,
// TODO: Support sensitive data
})
}
result.Name = res.Result.Name
result.Kind = res.Result.Kind
result.Details = res.Result.Details
result.ParentObject = res.Result.ParentObject
result.Error = errorsFound
}
return result, nil
}

10
pkg/custom/types.go Normal file
View File

@@ -0,0 +1,10 @@
package custom
type Connection struct {
Url string `json:"url"`
Port string `json:"port"`
}
type CustomAnalyzer struct {
Name string `json:"name"`
Connection Connection `json:"connection"`
}

View File

@@ -0,0 +1,85 @@
package aws
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/spf13/viper"
"os"
)
type AWS struct {
sess *session.Session
}
func (a *AWS) Deploy(namespace string) error {
return nil
}
func (a *AWS) UnDeploy(namespace string) error {
a.sess = nil
return nil
}
func (a *AWS) AddAnalyzer(mergedMap *map[string]common.IAnalyzer) {
// Check for AWS credentials in the environment
// https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html
if os.Getenv("AWS_ACCESS_KEY_ID") == "" || os.Getenv("AWS_SECRET_ACCESS_KEY") == "" {
panic("AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be set in the environment")
}
sess := session.Must(session.NewSessionWithOptions(session.Options{
SharedConfigState: session.SharedConfigEnable,
Config: aws.Config{},
}))
a.sess = sess
(*mergedMap)["EKS"] = &EKSAnalyzer{
session: a.sess,
}
}
func (a *AWS) GetAnalyzerName() []string {
return []string{"EKS"}
}
func (a *AWS) GetNamespace() (string, error) {
return "", nil
}
func (a *AWS) OwnsAnalyzer(s string) bool {
for _, az := range a.GetAnalyzerName() {
if s == az {
return true
}
}
return false
}
func (a *AWS) isFilterActive() bool {
activeFilters := viper.GetStringSlice("active_filters")
for _, filter := range a.GetAnalyzerName() {
for _, af := range activeFilters {
if af == filter {
return true
}
}
}
return false
}
func (a *AWS) IsActivate() bool {
if a.isFilterActive() {
return true
} else {
return false
}
}
func NewAWS() *AWS {
return &AWS{}
}

View File

@@ -0,0 +1,80 @@
package aws
import (
"errors"
"github.com/spf13/viper"
"os"
"path/filepath"
"strings"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/eks"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"k8s.io/client-go/tools/clientcmd"
)
type EKSAnalyzer struct {
session *session.Session
}
func (e *EKSAnalyzer) Analyze(analysis common.Analyzer) ([]common.Result, error) {
var cr []common.Result = []common.Result{}
_ = map[string]common.PreAnalysis{}
svc := eks.New(e.session)
// Get the name of the current cluster
var kubeconfig string
kubeconfigFromPath := viper.GetString("kubeconfig")
if kubeconfigFromPath != "" {
kubeconfig = kubeconfigFromPath
} else {
kubeconfig = filepath.Join(os.Getenv("HOME"), ".kube", "config")
}
config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
&clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig},
&clientcmd.ConfigOverrides{
CurrentContext: "",
}).RawConfig()
if err != nil {
return cr, err
}
currentConfig := config.CurrentContext
if !strings.Contains(currentConfig, "eks") {
return cr, errors.New("EKS cluster was not detected")
}
input := &eks.ListClustersInput{}
result, err := svc.ListClusters(input)
if err != nil {
return cr, err
}
for _, cluster := range result.Clusters {
// describe the cluster
if !strings.Contains(currentConfig, *cluster) {
continue
}
input := &eks.DescribeClusterInput{
Name: cluster,
}
result, err := svc.DescribeCluster(input)
if err != nil {
return cr, err
}
if len(result.Cluster.Health.Issues) > 0 {
for _, issue := range result.Cluster.Health.Issues {
err := make([]common.Failure, 0)
err = append(err, common.Failure{
Text: issue.String(),
KubernetesDoc: "",
Sensitive: nil,
})
cr = append(cr, common.Result{
Kind: "EKS",
Name: "AWS/EKS",
Error: err,
})
}
}
}
return cr, nil
}

View File

@@ -16,8 +16,10 @@ package integration
import (
"errors"
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/integration/aws"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/integration/prometheus"
"github.com/k8sgpt-ai/k8sgpt/pkg/integration/trivy"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
"github.com/spf13/viper"
@@ -44,7 +46,9 @@ type Integration struct {
}
var integrations = map[string]IIntegration{
"trivy": trivy.NewTrivy(),
"trivy": trivy.NewTrivy(),
"prometheus": prometheus.NewPrometheus(),
"aws": aws.NewAWS(),
}
func NewIntegration() *Integration {

View File

@@ -0,0 +1,290 @@
package prometheus
import (
"bytes"
"compress/gzip"
"context"
"errors"
"fmt"
"io"
"net/http"
"path/filepath"
"strings"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
promconfig "github.com/prometheus/prometheus/config"
yaml "gopkg.in/yaml.v2"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
)
const (
prometheusContainerName = "prometheus"
configReloaderContainerName = "config-reloader"
prometheusConfigFlag = "--config.file="
configReloaderConfigFlag = "--config-file="
)
var prometheusPodLabels = map[string]string{
"app": "prometheus",
"app.kubernetes.io/name": "prometheus",
}
type ConfigAnalyzer struct {
}
// podConfig groups a specific pod with the Prometheus configuration and any
// other state used for informing the common.Result.
type podConfig struct {
b []byte
pod *corev1.Pod
}
func (c *ConfigAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
ctx := a.Context
client := a.Client.GetClient()
namespace := a.Namespace
kind := ConfigValidate
podConfigs, err := findPrometheusPodConfigs(ctx, client, namespace)
if err != nil {
return nil, err
}
var preAnalysis = map[string]common.PreAnalysis{}
for _, pc := range podConfigs {
var failures []common.Failure
pod := pc.pod
// Check upstream validation.
// The Prometheus configuration structs do not generally have validation
// methods and embed their validation logic in the UnmarshalYAML methods.
config, err := unmarshalPromConfigBytes(pc.b)
if err != nil {
failures = append(failures, common.Failure{
Text: fmt.Sprintf("error validating Prometheus YAML configuration: %s", err),
})
}
_, err = yaml.Marshal(config)
if err != nil {
failures = append(failures, common.Failure{
Text: fmt.Sprintf("error validating Prometheus struct configuration: %s", err),
})
}
// Check for empty scrape config.
if len(config.ScrapeConfigs) == 0 {
failures = append(failures, common.Failure{
Text: "no scrape configurations. Prometheus will not scrape any metrics.",
})
}
if len(failures) > 0 {
preAnalysis[fmt.Sprintf("%s/%s", pod.Namespace, pod.Name)] = common.PreAnalysis{
Pod: *pod,
FailureDetails: failures,
}
}
}
for key, value := range preAnalysis {
var currentAnalysis = common.Result{
Kind: kind,
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 configKey(namespace string, volume *corev1.Volume) (string, error) {
if volume.ConfigMap != nil {
return fmt.Sprintf("configmap/%s/%s", namespace, volume.ConfigMap.Name), nil
} else if volume.Secret != nil {
return fmt.Sprintf("secret/%s/%s", namespace, volume.Secret.SecretName), nil
} else {
return "", errors.New("volume format must be ConfigMap or Secret")
}
}
func findPrometheusPodConfigs(ctx context.Context, client kubernetes.Interface, namespace string) ([]podConfig, error) {
var configs []podConfig
pods, err := findPrometheusPods(ctx, client, namespace)
if err != nil {
return nil, err
}
var configCache = make(map[string]bool)
for _, pod := range pods {
// Extract volume of Prometheus config.
volume, key, err := findPrometheusConfigVolumeAndKey(ctx, client, &pod)
if err != nil {
return nil, err
}
// See if we processed it already; if so, don't process again.
ck, err := configKey(pod.Namespace, volume)
if err != nil {
return nil, err
}
_, ok := configCache[ck]
if ok {
continue
}
configCache[ck] = true
// Extract Prometheus config bytes from volume.
b, err := extractPrometheusConfigFromVolume(ctx, client, volume, pod.Namespace, key)
if err != nil {
return nil, err
}
configs = append(configs, podConfig{
pod: &pod,
b: b,
})
}
return configs, nil
}
func findPrometheusPods(ctx context.Context, client kubernetes.Interface, namespace string) ([]corev1.Pod, error) {
var proms []corev1.Pod
for k, v := range prometheusPodLabels {
pods, err := util.GetPodListByLabels(client, namespace, map[string]string{
k: v,
})
if err != nil {
return nil, err
}
proms = append(proms, pods.Items...)
}
// If we still haven't found any Prometheus pods, make a last-ditch effort to
// scrape the namespace for "prometheus" containers.
if len(proms) == 0 {
pods, err := client.CoreV1().Pods(namespace).List(ctx, v1.ListOptions{})
if err != nil {
return nil, err
}
for _, pod := range pods.Items {
for _, c := range pod.Spec.Containers {
if c.Name == prometheusContainerName {
proms = append(proms, pod)
}
}
}
}
return proms, nil
}
func findPrometheusConfigPath(ctx context.Context, client kubernetes.Interface, pod *corev1.Pod) (string, error) {
var path string
var err error
for _, container := range pod.Spec.Containers {
for _, arg := range container.Args {
// Prefer the config-reloader container config file as it normally
// references the ConfigMap or Secret volume mount.
// Fallback to the prometheus container if that's not found.
if strings.HasPrefix(arg, prometheusConfigFlag) {
path = strings.TrimPrefix(arg, prometheusConfigFlag)
}
if strings.HasPrefix(arg, configReloaderConfigFlag) {
path = strings.TrimPrefix(arg, configReloaderConfigFlag)
}
}
if container.Name == configReloaderContainerName {
return path, nil
}
}
if path == "" {
err = fmt.Errorf("prometheus config path not found in pod: %s", pod.Name)
}
return path, err
}
func findPrometheusConfigVolumeAndKey(ctx context.Context, client kubernetes.Interface, pod *corev1.Pod) (*corev1.Volume, string, error) {
path, err := findPrometheusConfigPath(ctx, client, pod)
if err != nil {
return nil, "", err
}
// Find the volumeMount the config path is pointing to.
var volumeName = ""
for _, container := range pod.Spec.Containers {
for _, vm := range container.VolumeMounts {
if strings.HasPrefix(path, vm.MountPath) {
volumeName = vm.Name
break
}
}
}
// Get the actual Volume from the name.
for _, volume := range pod.Spec.Volumes {
if volume.Name == volumeName {
return &volume, filepath.Base(path), nil
}
}
return nil, "", errors.New("volume for Prometheus config not found")
}
func extractPrometheusConfigFromVolume(ctx context.Context, client kubernetes.Interface, volume *corev1.Volume, namespace, key string) ([]byte, error) {
var b []byte
var ok bool
// Check for Secret volume.
if vs := volume.Secret; vs != nil {
s, err := client.CoreV1().Secrets(namespace).Get(ctx, vs.SecretName, v1.GetOptions{})
if err != nil {
return nil, err
}
b, ok = s.Data[key]
if !ok {
return nil, fmt.Errorf("unable to find file key in secret: %s", key)
}
}
// Check for ConfigMap volume.
if vcm := volume.ConfigMap; vcm != nil {
cm, err := client.CoreV1().ConfigMaps(namespace).Get(ctx, vcm.Name, v1.GetOptions{})
if err != nil {
return nil, err
}
s, ok := cm.Data[key]
b = []byte(s)
if !ok {
return nil, fmt.Errorf("unable to find file key in configmap: %s", key)
}
}
return b, nil
}
func unmarshalPromConfigBytes(b []byte) (*promconfig.Config, error) {
var config promconfig.Config
// Unmarshal the data into a Prometheus config.
if err := yaml.Unmarshal(b, &config); err == nil {
return &config, nil
// If there were errors, try gunziping the data.
} else if content := http.DetectContentType(b); content == "application/x-gzip" {
r, err := gzip.NewReader(bytes.NewBuffer(b))
if err != nil {
return &config, err
}
gunzipBytes, err := io.ReadAll(r)
if err != nil {
return &config, err
}
err = yaml.Unmarshal(gunzipBytes, &config)
if err != nil {
return nil, err
}
return &config, nil
} else {
return &config, err
}
}

View File

@@ -0,0 +1,105 @@
package prometheus
import (
"context"
"errors"
"fmt"
"os"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/spf13/viper"
)
const (
ConfigValidate = "PrometheusConfigValidate"
ConfigRelabel = "PrometheusConfigRelabelReport"
)
type Prometheus struct {
}
func NewPrometheus() *Prometheus {
return &Prometheus{}
}
func (p *Prometheus) Deploy(namespace string) error {
// no-op
color.Green("Activating prometheus integration...")
// TODO(pintohutch): add timeout or inherit an upstream context
// for better signal management.
ctx := context.Background()
kubecontext := viper.GetString("kubecontext")
kubeconfig := viper.GetString("kubeconfig")
client, err := kubernetes.NewClient(kubecontext, kubeconfig)
if err != nil {
color.Red("Error initialising kubernetes client: %v", err)
os.Exit(1)
}
// We just care about existing deployments.
// Try and find Prometheus configurations in the cluster using the provided namespace.
//
// Note: We could cache this state and inject it into the various analyzers
// to save additional parsing later.
// However, the state of the cluster can change from activation to analysis,
// so we would want to run this again on each analyze call anyway.
//
// One consequence of this is one can run `activate` in one namespace
// and run `analyze` in another, without issues, as long as Prometheus
// is found in both.
// We accept this as a trade-off for the time-being to avoid having the tool
// manage Prometheus on the behalf of users.
podConfigs, err := findPrometheusPodConfigs(ctx, client.GetClient(), namespace)
if err != nil {
color.Red("Error discovering Prometheus worklads: %v", err)
os.Exit(1)
}
if len(podConfigs) == 0 {
color.Yellow(fmt.Sprintf(`Prometheus installation not found in namespace: %s.
Please ensure Prometheus is deployed to analyze.`, namespace))
return errors.New("no prometheus installation found")
}
// Prime state of the analyzer so
color.Green("Found existing installation")
return nil
}
func (p *Prometheus) UnDeploy(_ string) error {
// no-op
// We just care about existing deployments.
color.Yellow("Integration will leave Prometheus resources deployed. This is an effective no-op in the cluster.")
return nil
}
func (p *Prometheus) AddAnalyzer(mergedMap *map[string]common.IAnalyzer) {
(*mergedMap)[ConfigValidate] = &ConfigAnalyzer{}
(*mergedMap)[ConfigRelabel] = &RelabelAnalyzer{}
}
func (p *Prometheus) GetAnalyzerName() []string {
return []string{ConfigValidate, ConfigRelabel}
}
func (p *Prometheus) GetNamespace() (string, error) {
return "", nil
}
func (p *Prometheus) OwnsAnalyzer(analyzer string) bool {
return (analyzer == ConfigValidate) || (analyzer == ConfigRelabel)
}
func (t *Prometheus) IsActivate() bool {
activeFilters := viper.GetStringSlice("active_filters")
for _, filter := range t.GetAnalyzerName() {
for _, af := range activeFilters {
if af == filter {
return true
}
}
}
return false
}

View File

@@ -0,0 +1,85 @@
package prometheus
import (
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
discoverykube "github.com/prometheus/prometheus/discovery/kubernetes"
"gopkg.in/yaml.v2"
)
type RelabelAnalyzer struct {
}
func (r *RelabelAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
ctx := a.Context
client := a.Client.GetClient()
namespace := a.Namespace
kind := ConfigRelabel
podConfigs, err := findPrometheusPodConfigs(ctx, client, namespace)
if err != nil {
return nil, err
}
var preAnalysis = map[string]common.PreAnalysis{}
for _, pc := range podConfigs {
var failures []common.Failure
pod := pc.pod
// Check upstream validation.
// The Prometheus configuration structs do not generally have validation
// methods and embed their validation logic in the UnmarshalYAML methods.
config, _ := unmarshalPromConfigBytes(pc.b)
// Limit output for brevity.
limit := 6
i := 0
for _, sc := range config.ScrapeConfigs {
if i == limit {
break
}
if sc == nil {
continue
}
brc, _ := yaml.Marshal(sc.RelabelConfigs)
var bsd []byte
for _, cfg := range sc.ServiceDiscoveryConfigs {
ks, ok := cfg.(*discoverykube.SDConfig)
if !ok {
continue
}
bsd, _ = yaml.Marshal(ks)
}
// Don't bother with relabel analysis if the scrape config
// or service discovery config are empty.
if len(brc) == 0 || len(bsd) == 0 {
continue
}
failures = append(failures, common.Failure{
Text: fmt.Sprintf("job_name:\n%s\nrelabel_configs:\n%s\nkubernetes_sd_configs:\n%s\n", sc.JobName, string(brc), string(bsd)),
})
i++
}
if len(failures) > 0 {
preAnalysis[fmt.Sprintf("%s/%s", pod.Namespace, pod.Name)] = common.PreAnalysis{
Pod: *pod,
FailureDetails: failures,
}
}
}
for key, value := range preAnalysis {
var currentAnalysis = common.Result{
Kind: kind,
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
}

View File

@@ -15,9 +15,10 @@ package trivy
import (
"fmt"
ctrl "sigs.k8s.io/controller-runtime/pkg/client"
"strings"
ctrl "sigs.k8s.io/controller-runtime/pkg/client"
"github.com/aquasecurity/trivy-operator/pkg/apis/aquasecurity/v1alpha1"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
@@ -33,7 +34,10 @@ func (TrivyAnalyzer) analyzeVulnerabilityReports(a common.Analyzer) ([]common.Re
result := &v1alpha1.VulnerabilityReportList{}
client := a.Client.CtrlClient
v1alpha1.AddToScheme(client.Scheme())
err := v1alpha1.AddToScheme(client.Scheme())
if err != nil {
return nil, err
}
if err := client.List(a.Context, result, &ctrl.ListOptions{}); err != nil {
return nil, err
}
@@ -56,8 +60,8 @@ func (TrivyAnalyzer) analyzeVulnerabilityReports(a common.Analyzer) ([]common.Re
}
}
if len(failures) > 0 {
preAnalysis[fmt.Sprintf("%s/%s", report.Labels["trivy-operator.resource.namespace"],
report.Labels["trivy-operator.resource.name"])] = common.PreAnalysis{
preAnalysis[fmt.Sprintf("%s/%s", report.Namespace,
report.Name)] = common.PreAnalysis{
TrivyVulnerabilityReport: report,
FailureDetails: failures,
}
@@ -85,7 +89,10 @@ func (t TrivyAnalyzer) analyzeConfigAuditReports(a common.Analyzer) ([]common.Re
result := &v1alpha1.ConfigAuditReportList{}
client := a.Client.CtrlClient
v1alpha1.AddToScheme(client.Scheme())
err := v1alpha1.AddToScheme(client.Scheme())
if err != nil {
return nil, err
}
if err := client.List(a.Context, result, &ctrl.ListOptions{}); err != nil {
return nil, err
}
@@ -116,8 +123,8 @@ func (t TrivyAnalyzer) analyzeConfigAuditReports(a common.Analyzer) ([]common.Re
}
if len(failures) > 0 {
preAnalysis[fmt.Sprintf("%s/%s", report.Labels["trivy-operator.resource.namespace"],
report.Labels["trivy-operator.resource.name"])] = common.PreAnalysis{
preAnalysis[fmt.Sprintf("%s/%s", report.Namespace,
report.Name)] = common.PreAnalysis{
TrivyConfigAuditReport: report,
FailureDetails: failures,
}

View File

@@ -0,0 +1,106 @@
/*
Copyright 2024 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package kubernetes
import (
"testing"
openapi_v2 "github.com/google/gnostic/openapiv2"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/runtime/schema"
)
func TestGetApiDocV2(t *testing.T) {
k8s := &K8sApiReference{
ApiVersion: schema.GroupVersion{
Group: "group.v1",
Version: "v1",
},
OpenapiSchema: &openapi_v2.Document{
Definitions: &openapi_v2.Definitions{
AdditionalProperties: []*openapi_v2.NamedSchema{
{
Name: "group.v1.kind",
Value: &openapi_v2.Schema{
Title: "test",
Properties: &openapi_v2.Properties{
AdditionalProperties: []*openapi_v2.NamedSchema{
{
Name: "schema1",
Value: &openapi_v2.Schema{
Title: "test",
Description: "schema1 description",
Type: &openapi_v2.TypeItem{
Value: []string{"string"},
},
},
},
{
Name: "schema2",
Value: &openapi_v2.Schema{
Items: &openapi_v2.ItemsItem{
Schema: []*openapi_v2.Schema{
{
Title: "random-schema",
},
},
},
Title: "test",
XRef: "xref",
Description: "schema2 description",
Type: &openapi_v2.TypeItem{
Value: []string{"bool"},
},
},
},
},
},
},
},
{
Name: "group",
},
},
},
},
Kind: "kind",
}
tests := []struct {
name string
field string
expectedOutput string
}{
{
name: "empty field",
},
{
name: "2 schemas",
field: "schema2.schema1",
expectedOutput: "",
},
{
name: "schema1 description",
field: "schema1",
expectedOutput: "schema1 description",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
output := k8s.GetApiDocV2(tt.field)
require.Equal(t, tt.expectedOutput, output)
})
}
}

View File

@@ -0,0 +1,79 @@
/*
Copyright 2024 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package kubernetes
import (
"testing"
"github.com/stretchr/testify/require"
"k8s.io/client-go/rest"
)
func TestSliceContainsString(t *testing.T) {
tests := []struct {
name string
kubeContext string
kubeConfig string
expectedErr string
}{
{
name: "empty config and empty context",
kubeContext: "",
kubeConfig: "",
expectedErr: "invalid configuration: no configuration has been provided, try setting KUBERNETES_MASTER environment variable",
},
{
name: "non empty config and empty context",
kubeContext: "",
kubeConfig: "kube-config",
expectedErr: "stat kube-config: no such file or directory",
},
{
name: "empty config and non empty context",
kubeContext: "some-context",
kubeConfig: "",
expectedErr: "context \"some-context\" does not exist",
},
{
name: "non empty config and non empty context",
kubeContext: "minikube",
kubeConfig: "./testdata/kubeconfig",
expectedErr: "Get \"https://192.168.49.2:8443/version\": dial tcp 192.168.49.2:8443",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
client, err := NewClient(tt.kubeContext, tt.kubeConfig)
if tt.expectedErr == "" {
require.NoError(t, err)
} else {
require.ErrorContains(t, err, tt.expectedErr)
require.Nil(t, client)
}
})
}
}
func TestKubernetesClient(t *testing.T) {
client := Client{
Config: &rest.Config{
Host: "host",
},
}
require.NotEmpty(t, client.GetConfig())
require.Nil(t, client.GetClient())
require.Nil(t, client.GetCtrlClient())
}

16
pkg/kubernetes/testdata/kubeconfig vendored Normal file
View File

@@ -0,0 +1,16 @@
apiVersion: v1
clusters:
- cluster:
server: https://192.168.49.2:8443
name: minikube
contexts:
- context:
cluster: minikube
namespace: default
user: minikube
name: minikube
current-context: minikube
kind: Config
preferences: {}
users:
- name: minikube

View File

@@ -9,17 +9,13 @@ import (
)
func (h *handler) Analyze(ctx context.Context, i *schemav1.AnalyzeRequest) (
*schemav1.AnalyzeResponse,
error,
*schemav1.AnalyzeResponse,
error,
) {
if i.Output == "" {
i.Output = "json"
}
if i.Backend == "" {
i.Backend = "openai"
}
if int(i.MaxConcurrency) == 0 {
i.MaxConcurrency = 10
}
@@ -33,6 +29,7 @@ func (h *handler) Analyze(ctx context.Context, i *schemav1.AnalyzeRequest) (
i.Explain,
int(i.MaxConcurrency),
false, // Kubernetes Doc disabled in server mode
false, // Interactive mode disabled in server mode
)
config.Context = ctx // Replace context for correct timeouts.
if err != nil {

View File

@@ -38,6 +38,9 @@ func (h *handler) syncIntegration(ctx context.Context,
activeFilters = coreFilters
}
var err error = status.Error(codes.OK, "")
if err != nil {
fmt.Println(err)
}
deactivateFunc := func(integrationRef integration.IIntegration) error {
namespace, err := integrationRef.GetNamespace()
if err != nil {
@@ -125,13 +128,19 @@ func (*handler) deactivateAllIntegrations(integrationProvider *integration.Integ
b, _ := integrationProvider.IsActivate(i)
if b {
in, err := integrationProvider.Get(i)
if err != nil {
return err
}
namespace, err := in.GetNamespace()
if err != nil {
return err
}
if err == nil {
if namespace != "" {
integrationProvider.Deactivate(i, namespace)
err := integrationProvider.Deactivate(i, namespace)
if err != nil {
return err
}
} else {
fmt.Printf("Skipping deactivation of %s, not installed\n", i)
}

View File

@@ -14,35 +14,40 @@ limitations under the License.
package server
import (
json "encoding/json"
"context"
"errors"
"fmt"
"log"
"net"
"net/http"
"strconv"
"strings"
"time"
gw "buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc-ecosystem/gateway/v2/schema/v1/server-service/schemav1gateway"
rpc "buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go/schema/v1/schemav1grpc"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.uber.org/zap"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/reflection"
)
type Config struct {
Port string
MetricsPort string
Backend string
Key string
Token string
Output string
maxConcurrency int
Handler *handler
Logger *zap.Logger
metricsServer *http.Server
listener net.Listener
Port string
MetricsPort string
Backend string
Key string
Token string
Output string
Handler *handler
Logger *zap.Logger
metricsServer *http.Server
listener net.Listener
EnableHttp bool
}
type Health struct {
@@ -51,6 +56,7 @@ type Health struct {
Failure int `json:"failure"`
}
//nolint:unused
var health = Health{
Status: "ok",
Success: 0,
@@ -61,8 +67,19 @@ func (s *Config) Shutdown() error {
return s.listener.Close()
}
func (s *Config) Serve() error {
// grpcHandlerFunc returns an http.Handler that delegates to grpcServer on incoming gRPC
// connections or otherHandler otherwise.
func grpcHandlerFunc(grpcServer *grpc.Server, otherHandler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
grpcServer.ServeHTTP(w, r)
} else {
otherHandler.ServeHTTP(w, r)
}
})
}
func (s *Config) Serve() error {
var lis net.Listener
var err error
address := fmt.Sprintf(":%s", s.Port)
@@ -70,16 +87,36 @@ func (s *Config) Serve() error {
if err != nil {
return err
}
s.listener = lis
s.Logger.Info(fmt.Sprintf("binding api to %s", s.Port))
grpcServerUnaryInterceptor := grpc.UnaryInterceptor(logInterceptor(s.Logger))
grpcServer := grpc.NewServer(grpcServerUnaryInterceptor)
reflection.Register(grpcServer)
rpc.RegisterServerServiceServer(grpcServer, s.Handler)
if err := grpcServer.Serve(
lis,
); err != nil && !errors.Is(err, http.ErrServerClosed) {
return err
if s.EnableHttp {
s.Logger.Info("enabling rest/http api")
gwmux := runtime.NewServeMux()
err = gw.RegisterServerServiceHandlerFromEndpoint(context.Background(), gwmux, fmt.Sprintf("localhost:%s", s.Port), []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())})
if err != nil {
log.Fatalln("Failed to register gateway:", err)
}
srv := &http.Server{
Addr: address,
Handler: h2c.NewHandler(grpcHandlerFunc(grpcServer, gwmux), &http2.Server{}),
}
if err := srv.Serve(lis); err != nil {
return err
}
} else {
if err := grpcServer.Serve(
lis,
); err != nil && !errors.Is(err, http.ErrServerClosed) {
return err
}
}
return nil
@@ -106,21 +143,3 @@ func (s *Config) ServeMetrics() error {
}
return nil
}
func (s *Config) healthzHandler(w http.ResponseWriter, r *http.Request) {
js, err := json.MarshalIndent(health, "", " ")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Fprint(w, string(js))
}
func getBoolParam(param string) bool {
b, err := strconv.ParseBool(strings.ToLower(param))
if err != nil {
// Handle error if conversion fails
return false
}
return b
}

View File

@@ -2,7 +2,6 @@ package server
import (
"os"
"sync"
"testing"
"github.com/fatih/color"
@@ -16,6 +15,7 @@ func TestServerInit(t *testing.T) {
color.Red("failed to create logger: %v", err)
os.Exit(1)
}
//nolint:all
defer logger.Sync()
server_config := Config{
Backend: "openai",
@@ -24,19 +24,15 @@ func TestServerInit(t *testing.T) {
Token: "none",
Logger: logger,
}
var wg sync.WaitGroup
go func() {
wg.Add(1)
err := server_config.Serve()
if err != nil {
assert.Fail(t, "serve: %s", err.Error())
}
server_config.Shutdown()
err = server_config.Shutdown()
if err != nil {
assert.Fail(t, "shutdown: %s", err.Error())
}
wg.Done()
}()
wg.Wait()
}

View File

@@ -23,6 +23,7 @@ import (
"fmt"
"os"
"regexp"
"strings"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
v1 "k8s.io/api/core/v1"
@@ -216,11 +217,18 @@ func EnsureDirExists(dir string) error {
}
func MapToString(m map[string]string) string {
var result string
for k, v := range m {
result += fmt.Sprintf("%s=%s,", k, v)
// Handle empty map case
if len(m) == 0 {
return ""
}
return result[:len(result)-1]
var pairs []string
for k, v := range m {
pairs = append(pairs, fmt.Sprintf("%s=%s", k, v))
}
// Efficient string joining
return strings.Join(pairs, ",")
}
func LabelsIncludeAny(predefinedSelector, Labels map[string]string) bool {

523
pkg/util/util_test.go Normal file
View File

@@ -0,0 +1,523 @@
/*
Copyright 2024 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"testing"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/stretchr/testify/require"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
appsv1 "k8s.io/api/apps/v1"
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"
)
func TestSliceContainsString(t *testing.T) {
tests := []struct {
slice []string
s string
expected bool
}{
{
expected: false,
},
{
slice: []string{"temp", "value"},
s: "value",
expected: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.s, func(t *testing.T) {
require.Equal(t, tt.expected, SliceContainsString(tt.slice, tt.s))
})
}
}
func TestGetParent(t *testing.T) {
ownerName := "test-name"
namespace := "test"
clientset := fake.NewSimpleClientset(
&appsv1.ReplicaSet{
ObjectMeta: metav1.ObjectMeta{
Name: ownerName,
Namespace: namespace,
},
},
&appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: ownerName,
Namespace: namespace,
},
},
&appsv1.StatefulSet{
ObjectMeta: metav1.ObjectMeta{
Name: ownerName,
Namespace: namespace,
},
},
&appsv1.DaemonSet{
ObjectMeta: metav1.ObjectMeta{
Name: ownerName,
Namespace: namespace,
},
},
&networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: ownerName,
Namespace: namespace,
},
},
&admissionregistrationv1.MutatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: ownerName,
},
},
&admissionregistrationv1.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: ownerName,
},
},
)
kubeClient := kubernetes.Client{
Client: clientset,
}
tests := []struct {
name string
kind string
expectedOutput string
}{
{
kind: "Unknown",
expectedOutput: ownerName,
},
{
kind: "ReplicaSet",
},
{
kind: "ReplicaSet",
name: ownerName,
expectedOutput: "ReplicaSet/test-name",
},
{
kind: "Deployment",
},
{
kind: "Deployment",
name: ownerName,
expectedOutput: "Deployment/test-name",
},
{
kind: "StatefulSet",
},
{
kind: "StatefulSet",
name: ownerName,
expectedOutput: "StatefulSet/test-name",
},
{
kind: "DaemonSet",
},
{
kind: "DaemonSet",
name: ownerName,
expectedOutput: "DaemonSet/test-name",
},
{
kind: "Ingress",
},
{
kind: "Ingress",
name: ownerName,
expectedOutput: "Ingress/test-name",
},
{
kind: "MutatingWebhookConfiguration",
},
{
kind: "MutatingWebhookConfiguration",
name: ownerName,
expectedOutput: "MutatingWebhook/test-name",
},
{
kind: "ValidatingWebhookConfiguration",
},
{
kind: "ValidatingWebhookConfiguration",
name: ownerName,
expectedOutput: "ValidatingWebhook/test-name",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.kind, func(t *testing.T) {
meta := metav1.ObjectMeta{
Namespace: namespace,
Name: ownerName,
OwnerReferences: []metav1.OwnerReference{
{
Kind: tt.kind,
Name: tt.name,
},
},
}
output, ok := GetParent(&kubeClient, meta)
require.Equal(t, tt.expectedOutput, output)
require.Equal(t, false, ok)
})
}
}
func TestRemoveDuplicates(t *testing.T) {
tests := []struct {
name string
slice []string
expectedDuplicates []string
}{
{
name: "all empty",
expectedDuplicates: []string{},
},
{
name: "all unique",
slice: []string{"temp", "value"},
expectedDuplicates: []string{},
},
{
name: "slice not unique",
slice: []string{"temp", "mango", "mango"},
expectedDuplicates: []string{"mango"},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
_, duplicates := RemoveDuplicates(tt.slice)
require.Equal(t, tt.expectedDuplicates, duplicates)
})
}
}
func TestSliceDiff(t *testing.T) {
tests := []struct {
name string
source []string
dest []string
expectedDiff []string
}{
{
name: "all empty",
},
{
name: "non empty",
source: []string{"temp"},
dest: []string{"random"},
expectedDiff: []string{"temp"},
},
{
name: "no diff",
source: []string{"temp", "random"},
dest: []string{"random", "temp"},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.expectedDiff, SliceDiff(tt.source, tt.dest))
})
}
}
func TestReplaceIfMatch(t *testing.T) {
tests := []struct {
text string
pattern string
replacement string
expectedOutput string
}{
{
text: "",
expectedOutput: "",
},
{
text: "some value",
pattern: "new",
replacement: "latest",
expectedOutput: "some value",
},
{
text: "new value",
pattern: "value",
replacement: "day",
expectedOutput: "new day",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.text, func(t *testing.T) {
require.Equal(t, tt.expectedOutput, ReplaceIfMatch(tt.text, tt.pattern, tt.replacement))
})
}
}
func TestGetCacheKey(t *testing.T) {
tests := []struct {
provider string
language string
sEnc string
expectedOutput string
}{
{
expectedOutput: "d8156bae0c4243d3742fc4e9774d8aceabe0410249d720c855f98afc88ff846c",
},
{
provider: "provider",
language: "english",
sEnc: "encoding",
expectedOutput: "39415cc324b1553b93e80e46049e4e4dbb752dc7d0424b2c6ac96d745c6392aa",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.language, func(t *testing.T) {
require.Equal(t, tt.expectedOutput, GetCacheKey(tt.provider, tt.language, tt.sEnc))
})
}
}
func TestGetPodListByLabels(t *testing.T) {
namespace1 := "test1"
namespace2 := "test2"
clientset := fake.NewSimpleClientset(
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "Pod1",
Namespace: namespace1,
Labels: map[string]string{
"Name": "Pod1",
"Namespace": namespace1,
},
},
},
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "Pod2",
Namespace: namespace2,
Labels: map[string]string{
"Name": "Pod2",
"Namespace": namespace2,
},
},
},
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "Pod3",
Namespace: namespace1,
Labels: map[string]string{
"Name": "Pod3",
"Namespace": namespace1,
},
},
},
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "Pod4",
Namespace: namespace2,
Labels: map[string]string{
"Name": "Pod4",
"Namespace": namespace2,
},
},
},
)
tests := []struct {
name string
namespace string
labels map[string]string
expectedLen int
expectedErr string
}{
{
name: "Name is Pod1",
namespace: namespace1,
labels: map[string]string{
"Name": "Pod1",
},
expectedLen: 1,
},
{
name: "Name is Pod2 in namespace1",
namespace: namespace1,
labels: map[string]string{
"Name": "Pod2",
},
expectedLen: 0,
},
{
name: "Name is Pod2 in namespace 2",
namespace: namespace2,
labels: map[string]string{
"Name": "Pod2",
},
expectedLen: 1,
},
{
name: "All pods with namespace2 label in namespace1",
namespace: namespace1,
labels: map[string]string{
"Namespace": namespace2,
},
expectedLen: 0,
},
{
name: "All pods with namespace2 label in namespace2",
namespace: namespace2,
labels: map[string]string{
"Namespace": namespace2,
},
expectedLen: 2,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
pl, err := GetPodListByLabels(clientset, tt.namespace, tt.labels)
if tt.expectedErr == "" {
require.NoError(t, err)
require.Equal(t, tt.expectedLen, len(pl.Items))
} else {
require.ErrorContains(t, err, tt.expectedErr)
require.Nil(t, pl)
}
})
}
}
func TestFileExists(t *testing.T) {
tests := []struct {
filePath string
isPresent bool
err string
}{
{
filePath: "",
isPresent: false,
},
{
filePath: "./util.go",
isPresent: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.filePath, func(t *testing.T) {
isPresent, err := FileExists(tt.filePath)
if tt.err == "" {
require.NoError(t, err)
} else {
require.ErrorContains(t, err, tt.err)
}
require.Equal(t, tt.isPresent, isPresent)
})
}
}
func TestEnsureDirExists(t *testing.T) {
tests := []struct {
dir string
err string
}{
{
dir: "",
err: "mkdir : no such file or directory",
},
{
dir: "./",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.dir, func(t *testing.T) {
err := EnsureDirExists(tt.dir)
if tt.err == "" {
require.NoError(t, err)
} else {
require.ErrorContains(t, err, tt.err)
}
})
}
}
func TestMapToString(t *testing.T) {
tests := []struct {
name string
m map[string]string
output string
}{
{
name: "empty map",
m: map[string]string{},
},
{
name: "non-empty map",
m: map[string]string{
"key": "value",
},
output: "key=value",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.output, MapToString(tt.m))
})
}
}
func TestLabelsIncludeAny(t *testing.T) {
tests := []struct {
name string
m map[string]string
p map[string]string
ok bool
}{
{
name: "empty map",
m: map[string]string{},
p: map[string]string{},
ok: false,
},
{
name: "non-empty map",
m: map[string]string{
"key": "value",
},
p: map[string]string{
"key": "value",
},
ok: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.ok, LabelsIncludeAny(tt.p, tt.m))
})
}
}

View File

@@ -14,6 +14,31 @@
"automergeType": "pr",
"platformAutomerge": true,
"packageRules": [
{
"matchPackageNames": ["^github.com/Azure/azure-sdk-for-go/"],
"enabled": true,
"group": "azure-group"
},
{
"matchPackageNames": ["^github.com/prometheus/"],
"enabled": true,
"group": "prometheus-group"
},
{
"matchPackageNames": ["^k8s.io/", "^sigs.k8s.io/"],
"enabled": true,
"groupName": "kubernetes-group"
},
{
"matchPackageNames": ["^github.com/golang/","^golang.org"],
"enabled": true,
"group": "golang-group"
},
{
"matchPackageNames": ["^github.com/"],
"enabled": true,
"group": "github arbitrary dependencies"
},
{
"description": "Exclude retracted cohere-go versions: https://github.com/renovatebot/renovate/issues/13012",
"matchPackageNames": ["github.com/cohere-ai/cohere-go"],