Compare commits

..

63 Commits

Author SHA1 Message Date
github-actions[bot]
ffd017fbd7 chore(main): release 0.3.32 (#1119)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-20 09:29:10 +01:00
Rui Chen
e261c09889 fix: remove shorthand flag for topp option in add command (#1115)
Signed-off-by: Rui Chen <rui@chenrui.dev>
2024-05-20 09:06:35 +01:00
github-actions[bot]
cc890dfa46 chore(main): release 0.3.31 (#1077)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-16 16:23:41 +01:00
Anders Swanson
047afd46d6 feat: oci genai (#1102)
Signed-off-by: Anders Swanson <anders.swanson@oracle.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2024-05-16 15:44:41 +01:00
Vaibhav Malik
eda52312ae feat: implement Top-K sampling for improved user control (#1110)
This commit adds Top-K sampling, a feature that allows users to control
the randomness of the generated text by specifying the number of most
probable next words considered by the model. This enhances user control
and potentially improves the quality of the generated outputs.

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

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2024-05-16 15:41:07 +01:00
JuHyung Son
882c6f5225 feat: support AWS_PROFILE (#1114)
Signed-off-by: JuHyung-Son <sonju0427@gmail.com>
2024-05-16 15:36:33 +01:00
renovate[bot]
fe53907c44 fix(deps): update module golang.org/x/net to v0.25.0 (#1092)
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-05-09 07:49:49 +01:00
Vaibhav Malik
63f7fcfef7 test: added tests for the pkg/integration package (#904)
This commit adds new tests for the `pkg/integration` package. As a
result, the code the code coverage of the package has increased from 0%
to 100%

This also includes a minor adjustment in the error statements of the
`Activate` and `Deactive` functions to ensure better understanding of
the cause of the error.

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2024-05-09 07:18:59 +01:00
Vaibhav Malik
2c7c74472c test: added tests for the log analyzer (#1010)
* Added new tests for `LogAnalyzer` defined in the `pkg/analyzer`
  package. Increased the code coverage of the `log.go` file to >90%

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

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
2024-05-09 07:18:22 +01:00
renovate[bot]
3c4823127c fix(deps): update module github.com/aws/aws-sdk-go to v1.52.3 (#1094)
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-05-07 12:18:15 +01:00
Vaibhav Malik
c3a884f0c4 test: added missing tests for pkg/analysis package (#908)
This commit bumps the code coverage of the `pkg/analysis` package to
60.8%

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
Co-authored-by: JuHyung Son <sonju0427@gmail.com>
2024-05-05 17:24:26 +01:00
renovate[bot]
e74fc0838f fix(deps): update module github.com/sashabaranov/go-openai to v1.23.0 (#1091)
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-05-01 19:37:34 +01:00
renovate[bot]
f30c9f5554 fix(deps): update module github.com/google/generative-ai-go to v0.11.0 (#1089)
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-05-01 14:30:22 +01:00
renovate[bot]
36ccc62846 fix(deps): update module github.com/cohere-ai/cohere-go/v2 to v2.7.3 (#1087)
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-05-01 14:23:48 +01:00
renovate[bot]
a809a455f5 chore(deps): update amannn/action-semantic-pull-request action to v5.5.2 (#1088)
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-05-01 14:18:07 +01:00
renovate[bot]
12fa5aef4d chore(deps): update anchore/sbom-action action to v0.15.11 (#1082)
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-05-01 14:10:59 +01:00
renovate[bot]
75c2addf66 fix(deps): update module github.com/aws/aws-sdk-go to v1.51.32 (#1083)
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-05-01 14:05:39 +01:00
renovate[bot]
9b797d7e8b chore(deps): update actions/upload-artifact digest to 6546280 (#1079)
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-05-01 13:59:19 +01:00
renovate[bot]
820cd2e16c fix(deps): update module buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go to v1.3.0-20240406062209-1cc152efbf5c.3 (#1086)
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-05-01 11:33:41 +01:00
renovate[bot]
43953ffa34 fix(deps): update module github.com/azure/azure-sdk-for-go/sdk/storage/azblob to v1.3.2 (#1085)
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-05-01 09:12:47 +01:00
renovate[bot]
bd695d0987 fix(deps): update module github.com/azure/azure-sdk-for-go/sdk/azidentity to v1.5.2 (#1084)
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-04-29 20:14:39 +01:00
renovate[bot]
b12c006c63 chore(deps): update docker/build-push-action digest to 2cdde99 (#1032)
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-04-29 20:01:10 +01:00
renovate[bot]
e0af76f3c9 chore(deps): pin codecov/codecov-action action to ab904c4 (#1031)
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-04-29 19:55:39 +01:00
renovate[bot]
e894e778e9 fix(deps): update k8s.io/utils digest to 0849a56 (#1080)
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-04-29 10:53:52 +01:00
JuHyung Son
5cfe3325cb docs: add logAnalyzer in README.md (#1081)
Signed-off-by: JuHyung Son <sonju0427@gmail.com>
2024-04-28 09:08:43 +01:00
renovate[bot]
ea8183ce84 chore(deps): update actions/checkout digest to 0ad4b8f (#1078)
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-04-26 17:24:30 +01:00
renovate[bot]
24cff90a0c fix(deps): update module buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc-ecosystem/gateway/v2 to v2.19.1-20240406062209-1cc152efbf5c.1 (#1070)
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-04-26 14:44:25 +01:00
renovate[bot]
bf6f642c28 chore(deps): update google-github-actions/release-please-action action to v4.1.0 (#1045)
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-04-26 10:58:29 +01:00
github-actions[bot]
6279f358ca chore(main): release 0.3.30 (#1027)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-04-26 10:17:15 +01:00
Peter Pan
b2ab94375e fix: invalid ParentObj in output (#1068)
* Fix invalid ParentObj in output

Signed-off-by: Peter Pan <Peter.Pan@daocloud.io>

* fix UT as well for ParentObj changes

Signed-off-by: Peter Pan <Peter.Pan@daocloud.io>

* remove meta.Name in false output

Signed-off-by: Peter Pan <Peter.Pan@daocloud.io>

* fix UT as well

Signed-off-by: Peter Pan <Peter.Pan@daocloud.io>

---------

Signed-off-by: Peter Pan <Peter.Pan@daocloud.io>
Co-authored-by: Aris Boutselis <arisboutselis08@gmail.com>
2024-04-26 07:26:06 +01:00
DragonAlex98
9a73d1923f feat: add keda integration (#1058)
* refactor: move FetchLatestEvent inside util package

Signed-off-by: DragonAlex98 <a.antinori@reply.it>

* feat: add Keda integration and ScaledObject analyzer

Signed-off-by: DragonAlex98 <a.antinori@reply.it>

---------

Signed-off-by: DragonAlex98 <a.antinori@reply.it>
2024-04-25 11:41:01 +01:00
Christoph Enne
85a76a3be0 chore: update license file path to avoid conflicting installations (#878) (#1073)
Signed-off-by: christoph <christoph.enne@glasskube.eu>
Co-authored-by: Aris Boutselis <arisboutselis08@gmail.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2024-04-20 21:12:49 +01:00
Peter Pan
aa276a5379 feat: add Resource Kind in output (#1069)
Signed-off-by: Peter Pan <Peter.Pan@daocloud.io>
Co-authored-by: Aris Boutselis <arisboutselis08@gmail.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2024-04-20 21:11:02 +01:00
Miguel Varela Ramos
eb7687a089 chore(deps): update cohere client implementation to v2 (#1062)
* chore(deps): update cohere client implementation to v2 and to use chat endpoint

Signed-off-by: Miguel Varela Ramos <miguel@cohere.ai>

* chore: remove renovate rule for cohere-go

Signed-off-by: Miguel Varela Ramos <miguel@cohere.ai>

* style: remove unused attribute

Signed-off-by: Miguel Varela Ramos <miguel@cohere.ai>

* fix: go mod

Signed-off-by: Miguel Varela Ramos <miguel@cohere.ai>

---------

Signed-off-by: Miguel Varela Ramos <miguel@cohere.ai>
Signed-off-by: Miguel Varela Ramos <miguelvramos92@gmail.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2024-04-20 21:10:17 +01:00
Guido Muscioni
c162cc22ee fix: set topP from config (#1053)
* fix: set topP from config

Signed-off-by: “Guido <muscionig@gmail.com>

* style: correct format of openai ai provider

Signed-off-by: “Guido <muscionig@gmail.com>

* feat: set topP from the environment

Signed-off-by: “Guido <muscionig@gmail.com>

---------

Signed-off-by: “Guido <muscionig@gmail.com>
2024-04-19 16:38:52 +01:00
izturn
1ae4e75196 refactor: replace util.SliceContainsString with slices.Contains & make fmt (#1041)
* use std package's func instead

Signed-off-by: gang.liu <gang.liu@daocloud.io>

* refactor: replace util.SliceContainsString with slices.Contains  & make fmt

Signed-off-by: gang.liu <gang.liu@daocloud.io>

---------

Signed-off-by: gang.liu <gang.liu@daocloud.io>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2024-04-19 13:15:50 +01:00
renovate[bot]
693b23f1fc fix(deps): update module golang.org/x/net to v0.23.0 [security] (#1071)
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-04-19 12:04:55 +01:00
Aris Boutselis
e6085d4191 feat: add minio support (#1048)
* feat: add minio support

Signed-off-by: Aris Boutselis <arisboutselis08@gmail.com>

* feat: add TLS skip for custom https minio endpoint

Signed-off-by: Aris Boutselis <arisboutselis08@gmail.com>

* feat: update cache with the new proto schema

Signed-off-by: Aris Boutselis <arisboutselis08@gmail.com>

---------

Signed-off-by: Aris Boutselis <arisboutselis08@gmail.com>
2024-04-19 11:58:41 +01:00
renovate[bot]
3eaf776249 chore(deps): update docker/setup-buildx-action digest to d70bba7 (#1066)
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-04-17 13:55:03 +01:00
renovate[bot]
ccb692c1fd fix(deps): update module github.com/aws/aws-sdk-go to v1.51.21 (#1056)
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-04-16 07:55:03 +01:00
Kay Yan
9e0263778f fix: remove show password in auth list (#1061)
Signed-off-by: Kay Yan <kay.yan@daocloud.io>
Co-authored-by: Aris Boutselis <arisboutselis08@gmail.com>
2024-04-16 07:20:20 +01:00
Vaibhav Malik
9dfcce842e test: added missing tests for the Pod analyzer (#1021)
- Fixed a small bug where failures were being appended multiple times
  for CrashLoopBackOff and ContainerCreating container status reasons.

- Added missing test cases to ensure proper testing of the Pod analyzer.
  The addition of these missing test cases has increased the code
  coverage of this analyzer to 98%.

- Added checks for init containers in a pod.

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

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
2024-04-13 21:08:33 +01:00
renovate[bot]
6df0169491 fix(deps): update module cloud.google.com/go/storage to v1.40.0 (#1054)
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-04-05 11:34:51 +01:00
renovate[bot]
007b4bb8ec fix(deps): update module github.com/aws/aws-sdk-go to v1.51.14 (#1051)
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-04-04 11:23:42 +01:00
renovate[bot]
6b38a56afb fix(deps): update module github.com/google/generative-ai-go to v0.10.0 (#1047)
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-04-03 14:40:40 +01:00
renovate[bot]
19ae31b5dd fix(deps): update module github.com/aws/aws-sdk-go to v1.51.8 (#1046)
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-27 10:01:58 +00:00
renovate[bot]
6a46a26789 fix(deps): update module github.com/sashabaranov/go-openai to v1.20.4 (#1039)
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-27 09:48:52 +00:00
renovate[bot]
e05a902d90 chore(deps): update anchore/sbom-action action to v0.15.10 (#1044)
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-27 09:34:52 +00:00
renovate[bot]
a3896f4518 fix(deps): update module cloud.google.com/go/storage to v1.39.1 (#1029)
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-26 20:26:15 +00:00
Vaibhav Malik
4262c9292c test: removed pkg/kubernetes/testdata (#1018)
The `testdata` was no longer being used. Hence, removed it.

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
2024-03-26 13:48:28 +00:00
renovate[bot]
94cdce44b4 fix(deps): update k8s.io/utils digest to 4693a02 (#1037)
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-25 13:16:40 +00:00
renovate[bot]
10c00ba9fe chore(deps): update docker/setup-buildx-action digest to 2b51285 (#1036)
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-25 13:09:40 +00:00
renovate[bot]
c872e495ad chore(deps): update docker/login-action digest to e92390c (#1033)
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-25 13:04:55 +00:00
Aris Boutselis
bd2e06bae7 chore: update renovate config and bundle deps in groups (#1026)
Signed-off-by: Aris Boutselis <arisboutselis08@gmail.com>
2024-03-24 20:57:43 +00:00
github-actions[bot]
5db4bc28a7 chore(main): release 0.3.29 (#1024)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-03-22 10:26:55 +00:00
Alex Jones
8f8f5c6df7 chore: allows an environmental override of the default AWS region and… (#1025)
* chore: allows an environmental override of the default AWS region and using it for bedrock

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

* chore: missing provider region

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

---------

Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
2024-03-22 10:19:11 +00:00
Vaibhav Malik
3c1c055ac7 test: added missing tests for the CronJob analyzer (#1019)
* test: added missing tests for the CronJob analyzer

- Fixed a small bug where pre-analysis was incorrectly appended to the
  results every time at the end of the for loop. This caused the result
  for a single cronjob failure to be appended multiple times in the
  final results.

- Added missing test cases to ensure proper testing of the CronJob
  analyzer. The addition of these missing test cases has increased the
  code coverage of this analyzer to over 96%.

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

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>

* test: removed failure strings matching from tests

It is possible that the error or failure strings might change in the
future, causing the tests to fail. This commit addresses that issue by
removing the matching of failure text from various analyzer tests.

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>

---------

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
2024-03-21 18:13:32 +00:00
Vaibhav Malik
ebfbba98ca test: added missing test case for events.go (#1017)
With the addition of the latest changes, the missing test case when an
event happens after the currently set latest event has been covered.

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

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2024-03-21 11:42:12 +00:00
Vaibhav Malik
47463d4412 test: added missing tests for the Ingress analyzer (#1020)
- Added missing test cases to ensure proper testing of the Ingress
  analyzer. The addition of these missing test cases has increased the
  code coverage of this analyzer to over 97%.

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

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2024-03-21 11:02:19 +00:00
Alex Jones
fe81d16f75 feat: codecov (#1023)
* chore: missing schedule on auto merge

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

* feat: adding codecoverage back in

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

---------

Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
2024-03-20 11:07:33 +00:00
Vaibhav Malik
a1d0d0a180 test: added tests for the Node analyzer (#1014)
* Added new tests for the `Node` analyzer defined in the `pkg/analyzer`
  package.

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

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

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2024-03-20 09:32:23 +00:00
Vaibhav Malik
f60467cd4d test: added missing tests for the Netpool analyzer (#1016)
- Added a network policy allowing traffic to all pods. Resulting in
  additional failures in the results.

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

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2024-03-15 18:45:36 +00:00
Vaibhav Malik
20892b48d0 test: removed useless tests from pkg/kubernetes (#1015)
- This commit removes unnecessary tests defined in the pkg/kubernetes
package.

- The removed tests were found to be flaky and were causing a
  significant increase in CI time without adding much value to
  the codebase.

Signed-off-by: VaibhavMalik4187 <vaibhavmalik2018@gmail.com>
2024-03-14 21:51:15 +00:00
69 changed files with 2381 additions and 1056 deletions

View File

@@ -33,7 +33,7 @@ jobs:
steps:
- name: Check out code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4
- name: Extract branch name
id: extract_branch
@@ -70,14 +70,14 @@ jobs:
RELEASE_REGISTRY: "localhost:5000/k8sgpt"
steps:
- name: Check out code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@0d103c3126aa41d772a8362f6aa67afac040f80c # v3
uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3
- name: Build Docker Image
uses: docker/build-push-action@af5a7ed5ba88268d5278f7203fb52cd833f66d6e # v5
uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 # 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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4
with:
name: ${{ env.IMAGE_NAME }}-image.tar
path: /tmp/${{ env.IMAGE_NAME }}-image.tar
@@ -115,10 +115,10 @@ jobs:
contents: read # Needed for checking out the repository
steps:
- name: Check out code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4
- name: Login to GitHub Container Registry
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3
with:
registry: "ghcr.io"
username: ${{ github.actor }}
@@ -126,10 +126,10 @@ jobs:
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@0d103c3126aa41d772a8362f6aa67afac040f80c # v3
uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3
- name: Build Docker Image
uses: docker/build-push-action@af5a7ed5ba88268d5278f7203fb52cd833f66d6e # v5
uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 # v5
with:
context: .
file: ./container/Dockerfile

View File

@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4
- name: golangci-lint
uses: reviewdog/action-golangci-lint@00311c26a97213f93f2fd3a3524d66762e956ae0 # v2

View File

@@ -23,9 +23,9 @@ jobs:
# Release-please creates a PR that tracks all changes
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4
- uses: google-github-actions/release-please-action@cc61a07e2da466bebbc19b3a7dd01d6aecb20d1e # v4.0.2
- uses: google-github-actions/release-please-action@a37ac6e4f6449ce8b3f7607e4d97d0146028dc0b # v4.1.0
id: release
with:
command: manifest
@@ -41,7 +41,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4
with:
fetch-depth: 0
- name: Set up Go
@@ -49,7 +49,7 @@ jobs:
with:
go-version: '1.21'
- name: Download Syft
uses: anchore/sbom-action/download-syft@9fece9e20048ca9590af301449208b2b8861333b # v0.15.9
uses: anchore/sbom-action/download-syft@7ccf588e3cf3cc2611714c2eeae48550fbc17552 # v0.15.11
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5
with:
@@ -74,23 +74,23 @@ jobs:
IMAGE_NAME: k8sgpt
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4
with:
submodules: recursive
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@0d103c3126aa41d772a8362f6aa67afac040f80c # v3
uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3
- name: Login to GitHub Container Registry
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3
with:
registry: "ghcr.io"
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker Image
uses: docker/build-push-action@af5a7ed5ba88268d5278f7203fb52cd833f66d6e # v5
uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 # 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@9fece9e20048ca9590af301449208b2b8861333b # v0.15.9
uses: anchore/sbom-action@7ccf588e3cf3cc2611714c2eeae48550fbc17552 # v0.15.11
with:
image: ${{ env.IMAGE_TAG }}
artifact-name: sbom-${{ env.IMAGE_NAME }}

View File

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

View File

@@ -9,14 +9,13 @@ on:
- main
env:
GO_VERSION: "~1.21"
GO_VERSION: "~1.21"
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4
- name: Set up Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5
@@ -24,4 +23,8 @@ jobs:
go-version: ${{ env.GO_VERSION }}
- name: Run test
run: go test ./...
run: go test ./... -coverprofile=coverage.txt
- name: Upload coverage to Codecov
uses: codecov/codecov-action@ab904c41d6ece82784817410c45d8b8c02684457 # v3
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

View File

@@ -32,7 +32,7 @@ nfpms:
section: utils
contents:
- src: ./LICENSE
dst: /usr/share/doc/nfpm/copyright
dst: /usr/share/doc/k8sgpt/copyright
file_info:
mode: 0644

View File

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

View File

@@ -1,5 +1,105 @@
# Changelog
## [0.3.32](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.31...v0.3.32) (2024-05-20)
### Bug Fixes
* remove shorthand flag for topp option in add command ([#1115](https://github.com/k8sgpt-ai/k8sgpt/issues/1115)) ([e261c09](https://github.com/k8sgpt-ai/k8sgpt/commit/e261c09889359d5870acb9720ff033440f835f8f))
## [0.3.31](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.30...v0.3.31) (2024-05-16)
### Features
* implement Top-K sampling for improved user control ([#1110](https://github.com/k8sgpt-ai/k8sgpt/issues/1110)) ([eda5231](https://github.com/k8sgpt-ai/k8sgpt/commit/eda52312aef8113debbd770b8354c3a3cb1cc681))
* oci genai ([#1102](https://github.com/k8sgpt-ai/k8sgpt/issues/1102)) ([047afd4](https://github.com/k8sgpt-ai/k8sgpt/commit/047afd46d62d1bd1da1435550cbaf9daaca53aee))
* support AWS_PROFILE ([#1114](https://github.com/k8sgpt-ai/k8sgpt/issues/1114)) ([882c6f5](https://github.com/k8sgpt-ai/k8sgpt/commit/882c6f52252000da436e4fed9fd184b263f5a017))
### Bug Fixes
* **deps:** update k8s.io/utils digest to 0849a56 ([#1080](https://github.com/k8sgpt-ai/k8sgpt/issues/1080)) ([e894e77](https://github.com/k8sgpt-ai/k8sgpt/commit/e894e778e91d070448cd4a3f46dfc98dd588c9ed))
* **deps:** update module buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc-ecosystem/gateway/v2 to v2.19.1-20240406062209-1cc152efbf5c.1 ([#1070](https://github.com/k8sgpt-ai/k8sgpt/issues/1070)) ([24cff90](https://github.com/k8sgpt-ai/k8sgpt/commit/24cff90a0ca7488e48c94d13678529617c749aab))
* **deps:** update module buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go to v1.3.0-20240406062209-1cc152efbf5c.3 ([#1086](https://github.com/k8sgpt-ai/k8sgpt/issues/1086)) ([820cd2e](https://github.com/k8sgpt-ai/k8sgpt/commit/820cd2e16cbca2c89b56a4d2a69f95f3f5cd6c6b))
* **deps:** update module github.com/aws/aws-sdk-go to v1.51.32 ([#1083](https://github.com/k8sgpt-ai/k8sgpt/issues/1083)) ([75c2add](https://github.com/k8sgpt-ai/k8sgpt/commit/75c2addf66a54df57d0c0ac17f0b359f7612e446))
* **deps:** update module github.com/aws/aws-sdk-go to v1.52.3 ([#1094](https://github.com/k8sgpt-ai/k8sgpt/issues/1094)) ([3c48231](https://github.com/k8sgpt-ai/k8sgpt/commit/3c4823127ca04d1d280da6d932e951e6c3f71536))
* **deps:** update module github.com/azure/azure-sdk-for-go/sdk/azidentity to v1.5.2 ([#1084](https://github.com/k8sgpt-ai/k8sgpt/issues/1084)) ([bd695d0](https://github.com/k8sgpt-ai/k8sgpt/commit/bd695d0987e8ec12b44512c46bc5f2e5116076bd))
* **deps:** update module github.com/azure/azure-sdk-for-go/sdk/storage/azblob to v1.3.2 ([#1085](https://github.com/k8sgpt-ai/k8sgpt/issues/1085)) ([43953ff](https://github.com/k8sgpt-ai/k8sgpt/commit/43953ffa3412ae97b6d54ed14b94955d1b73feba))
* **deps:** update module github.com/cohere-ai/cohere-go/v2 to v2.7.3 ([#1087](https://github.com/k8sgpt-ai/k8sgpt/issues/1087)) ([36ccc62](https://github.com/k8sgpt-ai/k8sgpt/commit/36ccc628462ad102712fca115b56f521b2b33b38))
* **deps:** update module github.com/google/generative-ai-go to v0.11.0 ([#1089](https://github.com/k8sgpt-ai/k8sgpt/issues/1089)) ([f30c9f5](https://github.com/k8sgpt-ai/k8sgpt/commit/f30c9f555449bb90bf8242b88b8fae936cb57938))
* **deps:** update module github.com/sashabaranov/go-openai to v1.23.0 ([#1091](https://github.com/k8sgpt-ai/k8sgpt/issues/1091)) ([e74fc08](https://github.com/k8sgpt-ai/k8sgpt/commit/e74fc0838feac5a019a340f7c5ad1c9ae49913fa))
* **deps:** update module golang.org/x/net to v0.25.0 ([#1092](https://github.com/k8sgpt-ai/k8sgpt/issues/1092)) ([fe53907](https://github.com/k8sgpt-ai/k8sgpt/commit/fe53907c44e9cd56b6747f52ae3402bc6ae2bd49))
### Other
* **deps:** pin codecov/codecov-action action to ab904c4 ([#1031](https://github.com/k8sgpt-ai/k8sgpt/issues/1031)) ([e0af76f](https://github.com/k8sgpt-ai/k8sgpt/commit/e0af76f3c9c0120dbc4d9373d69a262e1ec2b7f2))
* **deps:** update actions/checkout digest to 0ad4b8f ([#1078](https://github.com/k8sgpt-ai/k8sgpt/issues/1078)) ([ea8183c](https://github.com/k8sgpt-ai/k8sgpt/commit/ea8183ce848ba58f91cfa68755d6f5b9cf695d36))
* **deps:** update actions/upload-artifact digest to 6546280 ([#1079](https://github.com/k8sgpt-ai/k8sgpt/issues/1079)) ([9b797d7](https://github.com/k8sgpt-ai/k8sgpt/commit/9b797d7e8b4f704dae12acaa7778b6b65e2c36ac))
* **deps:** update amannn/action-semantic-pull-request action to v5.5.2 ([#1088](https://github.com/k8sgpt-ai/k8sgpt/issues/1088)) ([a809a45](https://github.com/k8sgpt-ai/k8sgpt/commit/a809a455f55d1af104ebc0540007aa678581dd21))
* **deps:** update anchore/sbom-action action to v0.15.11 ([#1082](https://github.com/k8sgpt-ai/k8sgpt/issues/1082)) ([12fa5ae](https://github.com/k8sgpt-ai/k8sgpt/commit/12fa5aef4dada597d7059e5717ec7bee3b38c122))
* **deps:** update docker/build-push-action digest to 2cdde99 ([#1032](https://github.com/k8sgpt-ai/k8sgpt/issues/1032)) ([b12c006](https://github.com/k8sgpt-ai/k8sgpt/commit/b12c006c6304165269b90d770048b851e1aa1d1f))
* **deps:** update google-github-actions/release-please-action action to v4.1.0 ([#1045](https://github.com/k8sgpt-ai/k8sgpt/issues/1045)) ([bf6f642](https://github.com/k8sgpt-ai/k8sgpt/commit/bf6f642c280f640f2c9020b325e52670ced2cf50))
### Docs
* add logAnalyzer in README.md ([#1081](https://github.com/k8sgpt-ai/k8sgpt/issues/1081)) ([5cfe332](https://github.com/k8sgpt-ai/k8sgpt/commit/5cfe3325cb556cfb9d0532ae26727441c5177015))
## [0.3.30](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.29...v0.3.30) (2024-04-26)
### Features
* add keda integration ([#1058](https://github.com/k8sgpt-ai/k8sgpt/issues/1058)) ([9a73d19](https://github.com/k8sgpt-ai/k8sgpt/commit/9a73d1923f146aa1343465d89225e64bcb8e0112))
* add minio support ([#1048](https://github.com/k8sgpt-ai/k8sgpt/issues/1048)) ([e6085d4](https://github.com/k8sgpt-ai/k8sgpt/commit/e6085d4191a1695e295f4f6a2ac7219b67a37225))
* add Resource Kind in output ([#1069](https://github.com/k8sgpt-ai/k8sgpt/issues/1069)) ([aa276a5](https://github.com/k8sgpt-ai/k8sgpt/commit/aa276a5379b3d24a8e7a1f8b1193832df5a46220))
### Bug Fixes
* **deps:** update k8s.io/utils digest to 4693a02 ([#1037](https://github.com/k8sgpt-ai/k8sgpt/issues/1037)) ([94cdce4](https://github.com/k8sgpt-ai/k8sgpt/commit/94cdce44b49e0bb85e8b541688b2206e7c1dc33d))
* **deps:** update module cloud.google.com/go/storage to v1.39.1 ([#1029](https://github.com/k8sgpt-ai/k8sgpt/issues/1029)) ([a3896f4](https://github.com/k8sgpt-ai/k8sgpt/commit/a3896f4518ec6666a43de22a24a18f2b93c58073))
* **deps:** update module cloud.google.com/go/storage to v1.40.0 ([#1054](https://github.com/k8sgpt-ai/k8sgpt/issues/1054)) ([6df0169](https://github.com/k8sgpt-ai/k8sgpt/commit/6df01694916504cc4af3795361a4285098e2de85))
* **deps:** update module github.com/aws/aws-sdk-go to v1.51.14 ([#1051](https://github.com/k8sgpt-ai/k8sgpt/issues/1051)) ([007b4bb](https://github.com/k8sgpt-ai/k8sgpt/commit/007b4bb8ec4b36705f76fd2f5d96464c75915573))
* **deps:** update module github.com/aws/aws-sdk-go to v1.51.21 ([#1056](https://github.com/k8sgpt-ai/k8sgpt/issues/1056)) ([ccb692c](https://github.com/k8sgpt-ai/k8sgpt/commit/ccb692c1fdc5496d9d5810dfe41dbf1bdeb68d00))
* **deps:** update module github.com/aws/aws-sdk-go to v1.51.8 ([#1046](https://github.com/k8sgpt-ai/k8sgpt/issues/1046)) ([19ae31b](https://github.com/k8sgpt-ai/k8sgpt/commit/19ae31b5dd5c54413025cee8081d112223e38400))
* **deps:** update module github.com/google/generative-ai-go to v0.10.0 ([#1047](https://github.com/k8sgpt-ai/k8sgpt/issues/1047)) ([6b38a56](https://github.com/k8sgpt-ai/k8sgpt/commit/6b38a56afbdaa8e0d8f025088a52d3022673ef9d))
* **deps:** update module github.com/sashabaranov/go-openai to v1.20.4 ([#1039](https://github.com/k8sgpt-ai/k8sgpt/issues/1039)) ([6a46a26](https://github.com/k8sgpt-ai/k8sgpt/commit/6a46a26789f730d298cf49a706421f36bc8523b1))
* **deps:** update module golang.org/x/net to v0.23.0 [security] ([#1071](https://github.com/k8sgpt-ai/k8sgpt/issues/1071)) ([693b23f](https://github.com/k8sgpt-ai/k8sgpt/commit/693b23f1fc33659a3c4f52fc4d9c23348b22bfb1))
* invalid ParentObj in output ([#1068](https://github.com/k8sgpt-ai/k8sgpt/issues/1068)) ([b2ab943](https://github.com/k8sgpt-ai/k8sgpt/commit/b2ab94375e4233cdfa9762877995445c313bb962))
* remove show password in auth list ([#1061](https://github.com/k8sgpt-ai/k8sgpt/issues/1061)) ([9e02637](https://github.com/k8sgpt-ai/k8sgpt/commit/9e0263778f6dbc179184fa9d86f07d808283d63e))
* set topP from config ([#1053](https://github.com/k8sgpt-ai/k8sgpt/issues/1053)) ([c162cc2](https://github.com/k8sgpt-ai/k8sgpt/commit/c162cc22ee468070e0602d3fd684b022fa585c4f))
### Other
* **deps:** update anchore/sbom-action action to v0.15.10 ([#1044](https://github.com/k8sgpt-ai/k8sgpt/issues/1044)) ([e05a902](https://github.com/k8sgpt-ai/k8sgpt/commit/e05a902d904fc0b63998ae290f15e79d330317fb))
* **deps:** update cohere client implementation to v2 ([#1062](https://github.com/k8sgpt-ai/k8sgpt/issues/1062)) ([eb7687a](https://github.com/k8sgpt-ai/k8sgpt/commit/eb7687a08917ad4048c6f00c17bb45591a935a3a))
* **deps:** update docker/login-action digest to e92390c ([#1033](https://github.com/k8sgpt-ai/k8sgpt/issues/1033)) ([c872e49](https://github.com/k8sgpt-ai/k8sgpt/commit/c872e495ad6f787cf566a5b2f295deb3f08aba15))
* **deps:** update docker/setup-buildx-action digest to 2b51285 ([#1036](https://github.com/k8sgpt-ai/k8sgpt/issues/1036)) ([10c00ba](https://github.com/k8sgpt-ai/k8sgpt/commit/10c00ba9fe61a3ee1dc90d87dd7997da276905b4))
* **deps:** update docker/setup-buildx-action digest to d70bba7 ([#1066](https://github.com/k8sgpt-ai/k8sgpt/issues/1066)) ([3eaf776](https://github.com/k8sgpt-ai/k8sgpt/commit/3eaf776249719a0a13909d24e6b48deb6bf818b6))
* update license file path to avoid conflicting installations ([#878](https://github.com/k8sgpt-ai/k8sgpt/issues/878)) ([#1073](https://github.com/k8sgpt-ai/k8sgpt/issues/1073)) ([85a76a3](https://github.com/k8sgpt-ai/k8sgpt/commit/85a76a3be06df0ff713192d1f08fd01d1e8f219b))
* update renovate config and bundle deps in groups ([#1026](https://github.com/k8sgpt-ai/k8sgpt/issues/1026)) ([bd2e06b](https://github.com/k8sgpt-ai/k8sgpt/commit/bd2e06bae72528c5af1b4f44674d624d474d40dc))
### Refactoring
* replace util.SliceContainsString with slices.Contains & make fmt ([#1041](https://github.com/k8sgpt-ai/k8sgpt/issues/1041)) ([1ae4e75](https://github.com/k8sgpt-ai/k8sgpt/commit/1ae4e751967850e8146f8f3fa04c0dd302ef15bf))
## [0.3.29](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.28...v0.3.29) (2024-03-22)
### Features
* codecov ([#1023](https://github.com/k8sgpt-ai/k8sgpt/issues/1023)) ([fe81d16](https://github.com/k8sgpt-ai/k8sgpt/commit/fe81d16f756e5ea9db909e42e6caf1e17e040f86))
### Other
* allows an environmental override of the default AWS region and… ([#1025](https://github.com/k8sgpt-ai/k8sgpt/issues/1025)) ([8f8f5c6](https://github.com/k8sgpt-ai/k8sgpt/commit/8f8f5c6df7fbcd08ee48d91a4f2e011a3e69e4ac))
## [0.3.28](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.27...v0.3.28) (2024-03-14)

View File

@@ -41,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.28/k8sgpt_386.rpm
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.32/k8sgpt_386.rpm
sudo rpm -ivh k8sgpt_386.rpm
```
<!---x-release-please-end-->
@@ -50,7 +50,7 @@ brew install k8sgpt
<!---x-release-please-start-version-->
```
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.28/k8sgpt_amd64.rpm
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.32/k8sgpt_amd64.rpm
sudo rpm -ivh -i k8sgpt_amd64.rpm
```
<!---x-release-please-end-->
@@ -62,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.28/k8sgpt_386.deb
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.32/k8sgpt_386.deb
sudo dpkg -i k8sgpt_386.deb
```
<!---x-release-please-end-->
@@ -70,7 +70,7 @@ brew install k8sgpt
<!---x-release-please-start-version-->
```
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.28/k8sgpt_amd64.deb
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.32/k8sgpt_amd64.deb
sudo dpkg -i k8sgpt_amd64.deb
```
<!---x-release-please-end-->
@@ -83,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.28/k8sgpt_386.apk
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.32/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.28/k8sgpt_amd64.apk
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.32/k8sgpt_amd64.apk
apk add k8sgpt_amd64.apk
```
<!---x-release-please-end-->x
@@ -167,6 +167,7 @@ you will be able to write your own analyzers.
- [x] gatewayClass
- [x] gateway
- [x] httproute
- [x] logAnalyzer
## Examples
@@ -379,6 +380,7 @@ Note: **Anonymization does not currently apply to events.**
- RepicaSet
- PersistentVolumeClaim
- Pod
- Log
- **_*Events_**
***Note**:
@@ -431,6 +433,8 @@ _Adding a remote cache_
* AWS S3
* _As a prerequisite `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` are required as environmental variables._
* Configuration, ``` k8sgpt cache add s3 --region <aws region> --bucket <name> ```
* Minio Configuration with HTTP endpoint ``` k8sgpt cache add s3 --bucket <name> --endpoint <http://localhost:9000>```
* Minio Configuration with HTTPs endpoint, skipping TLS verification ``` k8sgpt cache add s3 --bucket <name> --endpoint <https://localhost:9000> --insecure```
* K8sGPT will create the bucket if it does not exist
* Azure Storage
* We support a number of [techniques](https://learn.microsoft.com/en-us/azure/developer/go/azure-sdk-authentication?tabs=bash#2-authenticate-with-azure) to authenticate against Azure

View File

@@ -45,6 +45,9 @@ var addCmd = &cobra.Command{
_ = cmd.MarkFlagRequired("endpointname")
_ = cmd.MarkFlagRequired("providerRegion")
}
if strings.ToLower(backend) == "amazonbedrock" {
_ = cmd.MarkFlagRequired("providerRegion")
}
},
Run: func(cmd *cobra.Command, args []string) {
@@ -97,6 +100,10 @@ var addCmd = &cobra.Command{
color.Red("Error: topP ranges from 0 to 1.")
os.Exit(1)
}
if topK < 1 || topK > 100 {
color.Red("Error: topK ranges from 1 to 100.")
os.Exit(1)
}
if ai.NeedPassword(backend) && password == "" {
fmt.Printf("Enter %s Key: ", backend)
@@ -120,7 +127,9 @@ var addCmd = &cobra.Command{
Temperature: temperature,
ProviderRegion: providerRegion,
ProviderId: providerId,
CompartmentId: compartmentId,
TopP: topP,
TopK: topK,
MaxTokens: maxTokens,
}
@@ -152,7 +161,9 @@ func init() {
// add flag for endpointName
addCmd.Flags().StringVarP(&endpointName, "endpointname", "n", "", "Endpoint Name, e.g. `endpoint-xxxxxxxxxxxx` (only for amazonbedrock, amazonsagemaker backends)")
// add flag for topP
addCmd.Flags().Float32VarP(&topP, "topp", "c", 0.5, "Probability Cutoff: Set a threshold (0.0-1.0) to limit word choices. Higher values add randomness, lower values increase predictability.")
addCmd.Flags().Float32VarP(&topP, "topp", "", 0.5, "Probability Cutoff: Set a threshold (0.0-1.0) to limit word choices. Higher values add randomness, lower values increase predictability.")
// add flag for topK
addCmd.Flags().Int32VarP(&topK, "topk", "c", 50, "Sampling Cutoff: Set a threshold (1-100) to restrict the sampling process to the top K most probable words at each step. Higher values lead to greater variability, lower values increases predictability.")
// max tokens
addCmd.Flags().IntVarP(&maxTokens, "maxtokens", "l", 2048, "Specify a maximum output length. Adjust (1-...) to control text length. Higher values produce longer output, lower values limit length")
// add flag for temperature
@@ -163,4 +174,6 @@ func init() {
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)")
//add flag for OCI Compartment ID
addCmd.Flags().StringVarP(&compartmentId, "compartmentId", "k", "", "Compartment ID for generative AI model (only for oci backend)")
}

View File

@@ -28,7 +28,9 @@ var (
temperature float32
providerRegion string
providerId string
compartmentId string
topP float32
topK int32
maxTokens int
)

View File

@@ -24,7 +24,6 @@ import (
)
var details bool
var userInput string
var listCmd = &cobra.Command{
Use: "list",
@@ -39,11 +38,6 @@ var listCmd = &cobra.Command{
os.Exit(1)
}
if details {
fmt.Println("Show password ? (y/n)")
fmt.Scan(&userInput)
}
// Print the default if it is set
fmt.Print(color.YellowString("Default: \n"))
if configAI.DefaultProvider != "" {
@@ -66,7 +60,7 @@ var listCmd = &cobra.Command{
if details {
for _, provider := range configAI.Providers {
if provider.Name == aiBackend {
printDetails(provider, userInput)
printDetails(provider)
}
}
}
@@ -91,7 +85,7 @@ func init() {
listCmd.Flags().BoolVar(&details, "details", false, "Print active provider configuration details")
}
func printDetails(provider ai.AIProvider, userInput string) {
func printDetails(provider ai.AIProvider) {
if provider.Model != "" {
fmt.Printf(" - Model: %s\n", provider.Model)
}

11
cmd/cache/add.go vendored
View File

@@ -30,6 +30,8 @@ var (
storageAccount string
containerName string
projectId string
endpoint string
insecure bool
)
// addCmd represents the add command
@@ -48,7 +50,7 @@ var addCmd = &cobra.Command{
}
fmt.Println(color.YellowString("Adding remote based cache"))
cacheType := args[0]
remoteCache, err := cache.NewCacheProvider(cacheType, bucketname, region, storageAccount, containerName, projectId)
remoteCache, err := cache.NewCacheProvider(cacheType, bucketName, region, endpoint, storageAccount, containerName, projectId, insecure)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
@@ -63,9 +65,10 @@ var addCmd = &cobra.Command{
func init() {
CacheCmd.AddCommand(addCmd)
addCmd.Flags().StringVarP(&region, "region", "r", "", "The region to use for the AWS S3 or GCS cache")
addCmd.Flags().StringVarP(&bucketname, "bucket", "b", "", "The name of the AWS S3 bucket to use for the cache")
addCmd.MarkFlagsRequiredTogether("region", "bucket")
addCmd.Flags().StringVarP(&region, "region", "r", "us-east-1", "The region to use for the AWS S3 or GCS cache")
addCmd.Flags().StringVarP(&endpoint, "endpoint", "e", "", "The S3 or minio endpoint")
addCmd.Flags().BoolVarP(&insecure, "insecure", "i", false, "Skip TLS verification for S3/Minio custom endpoint")
addCmd.Flags().StringVarP(&bucketName, "bucket", "b", "", "The name of the AWS S3 bucket to use for the cache")
addCmd.Flags().StringVarP(&projectId, "projectid", "p", "", "The GCP project ID")
addCmd.Flags().StringVarP(&storageAccount, "storageacc", "s", "", "The Azure storage account name of the container")
addCmd.Flags().StringVarP(&containerName, "container", "c", "", "The Azure container name to use for the cache")

4
cmd/cache/cache.go vendored
View File

@@ -18,10 +18,6 @@ import (
"github.com/spf13/cobra"
)
var (
bucketname string
)
// cacheCmd represents the cache command
var CacheCmd = &cobra.Command{
Use: "cache",

View File

@@ -15,6 +15,7 @@ package filters
import (
"fmt"
"slices"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer"
@@ -40,10 +41,9 @@ var listCmd = &cobra.Command{
inactiveFilters := util.SliceDiff(availableFilters, activeFilters)
fmt.Print(color.YellowString("Active: \n"))
for _, filter := range activeFilters {
// if the filter is an integration, mark this differently
// but if the integration is inactive, remove
if util.SliceContainsString(integrationFilters, filter) {
if slices.Contains(integrationFilters, filter) {
fmt.Printf("> %s\n", color.BlueString("%s (integration)", filter))
} else {
// This strange bit of logic will loop through every integration via
@@ -60,13 +60,12 @@ var listCmd = &cobra.Command{
fmt.Print(color.YellowString("Unused: \n"))
for _, filter := range inactiveFilters {
// if the filter is an integration, mark this differently
if util.SliceContainsString(integrationFilters, filter) {
if slices.Contains(integrationFilters, filter) {
fmt.Printf("> %s\n", color.BlueString("%s (integration)", filter))
} else {
fmt.Printf("> %s\n", color.RedString(filter))
}
}
}
},
}

View File

@@ -27,6 +27,8 @@ import (
const (
defaultTemperature float32 = 0.7
defaultTopP float32 = 1.0
defaultTopK int32 = 50
)
var (
@@ -67,6 +69,38 @@ var ServeCmd = &cobra.Command{
}
return float32(temperature)
}
topP := func() float32 {
env := os.Getenv("K8SGPT_TOP_P")
if env == "" {
return defaultTopP
}
topP, err := strconv.ParseFloat(env, 32)
if err != nil {
color.Red("Unable to convert topP value: %v", err)
os.Exit(1)
}
if topP > 1.0 || topP < 0.0 {
color.Red("Error: topP ranges from 0 to 1.")
os.Exit(1)
}
return float32(topP)
}
topK := func() int32 {
env := os.Getenv("K8SGPT_TOP_K")
if env == "" {
return defaultTopK
}
topK, err := strconv.ParseFloat(env, 32)
if err != nil {
color.Red("Unable to convert topK value: %v", err)
os.Exit(1)
}
if topK < 10 || topK > 100 {
color.Red("Error: topK ranges from 1 to 100.")
os.Exit(1)
}
return int32(topK)
}
// Check for env injection
backend = os.Getenv("K8SGPT_BACKEND")
password := os.Getenv("K8SGPT_PASSWORD")
@@ -79,13 +113,15 @@ var ServeCmd = &cobra.Command{
envIsSet := backend != "" || password != "" || model != ""
if envIsSet {
aiProvider = &ai.AIProvider{
Name: backend,
Password: password,
Model: model,
BaseURL: baseURL,
Engine: engine,
Name: backend,
Password: password,
Model: model,
BaseURL: baseURL,
Engine: engine,
ProxyEndpoint: proxyEndpoint,
Temperature: temperature(),
Temperature: temperature(),
TopP: topP(),
TopK: topK(),
}
configAI.Providers = append(configAI.Providers, *aiProvider)

86
go.mod
View File

@@ -5,14 +5,15 @@ go 1.21
require (
github.com/aquasecurity/trivy-operator v0.17.1
github.com/fatih/color v1.16.0
github.com/kedacore/keda/v2 v2.11.2
github.com/magiconair/properties v1.8.7
github.com/mittwald/go-helm-client v0.12.5
github.com/sashabaranov/go-openai v1.20.2
github.com/sashabaranov/go-openai v1.23.0
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.9.0
golang.org/x/term v0.17.0
golang.org/x/term v0.20.0
helm.sh/helm/v3 v3.13.3
k8s.io/api v0.28.4
k8s.io/apimachinery v0.28.4
@@ -24,21 +25,23 @@ require (
require github.com/adrg/xdg v0.4.0
require (
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.8.0
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc-ecosystem/gateway/v2 v2.19.1-20240406062209-1cc152efbf5c.1
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.3.0-20240406062209-1cc152efbf5c.3
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.34.0-20240406062209-1cc152efbf5c.1
cloud.google.com/go/storage v1.40.0
cloud.google.com/go/vertexai v0.7.1
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2
github.com/aws/aws-sdk-go v1.52.3
github.com/cohere-ai/cohere-go/v2 v2.7.3
github.com/google/generative-ai-go v0.11.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
github.com/oracle/oci-go-sdk/v65 v65.65.1
github.com/prometheus/prometheus v0.49.1
github.com/pterm/pterm v0.12.79
google.golang.org/api v0.167.0
google.golang.org/api v0.172.0
gopkg.in/yaml.v2 v2.4.0
sigs.k8s.io/controller-runtime v0.16.3
sigs.k8s.io/gateway-api v1.0.0
@@ -48,35 +51,33 @@ require (
atomicgo.dev/cursor v0.2.0 // indirect
atomicgo.dev/keyboard v0.2.9 // indirect
atomicgo.dev/schedule v0.1.0 // indirect
cloud.google.com/go v0.112.0 // indirect
cloud.google.com/go/ai v0.3.0 // indirect
cloud.google.com/go/aiplatform v1.59.0 // indirect
cloud.google.com/go v0.112.1 // indirect
cloud.google.com/go/ai v0.3.5-0.20240409161017-ce55ad694f21 // indirect
cloud.google.com/go/aiplatform v1.60.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.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
cloud.google.com/go/iam v1.1.7 // indirect
cloud.google.com/go/longrunning v0.5.6 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 // 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/AzureAD/microsoft-authentication-library-for-go v1.2.2 // 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/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/gofrs/flock v0.8.1 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // 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.1 // indirect
github.com/googleapis/gax-go/v2 v2.12.3 // 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
@@ -88,17 +89,20 @@ require (
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/sony/gobreaker v0.5.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.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
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240401170217-c3f982113cda // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa // indirect
gopkg.in/evanphx/json-patch.v5 v5.7.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
knative.dev/pkg v0.0.0-20230616134650-eb63a40adfb0 // indirect
)
require (
@@ -143,7 +147,7 @@ require (
github.com/go-openapi/swag v0.22.4 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/gnostic v0.7.0
github.com/google/go-cmp v0.6.0 // indirect
@@ -212,23 +216,23 @@ 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.23.0 // indirect
go.opentelemetry.io/otel/trace v1.23.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.starlark.net v0.0.0-20231016134836-22325403fcb3 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.19.0 // indirect
golang.org/x/crypto v0.23.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/net v0.25.0
golang.org/x/oauth2 v0.18.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/sys v0.20.0 // indirect
golang.org/x/text v0.15.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.62.0
google.golang.org/protobuf v1.32.0 // indirect
google.golang.org/grpc v1.62.1
google.golang.org/protobuf v1.34.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
k8s.io/apiextensions-apiserver v0.28.4
@@ -237,7 +241,7 @@ require (
k8s.io/component-base v0.28.4 // 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-20240102154912-e7106e64919e
k8s.io/utils v0.0.0-20240423183400-0849a56e8f22
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

177
go.sum
View File

@@ -6,17 +6,15 @@ atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8=
atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ=
atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs=
atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU=
buf.build/gen/go/grpc-ecosystem/grpc-gateway/grpc-ecosystem/gateway/v2 v2.19.1-20231027202514-3f42134f4c56.1/go.mod h1:G1KIJUmG9QYamM5XhEUDdcTA3ea3m9DZWV7opQpb1IQ=
buf.build/gen/go/grpc-ecosystem/grpc-gateway/grpc/go v1.3.0-20231027202514-3f42134f4c56.2/go.mod h1:FL988BzZirjg37E5k8AJR39eQ6/n/esl7iEEF63xniA=
buf.build/gen/go/grpc-ecosystem/grpc-gateway/protocolbuffers/go v1.28.1-20231027202514-3f42134f4c56.4/go.mod h1:92ejKVTiuvnKoAtRlpJpIxKfloI935DDqhs0NCRx+KM=
buf.build/gen/go/grpc-ecosystem/grpc-gateway/protocolbuffers/go v1.32.0-20231027202514-3f42134f4c56.1/go.mod h1:XdxZqtnJr4q2vF8RLEhC903d6Nxxtz1vEMTyTAyvSUs=
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc-ecosystem/gateway/v2 v2.19.1-20240213144542-6e830f3fdf19.1 h1:NBmcLW3zqAhhuqtQx2XOCDcFUCVFejKKu4o/oIvz+rs=
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc-ecosystem/gateway/v2 v2.19.1-20240213144542-6e830f3fdf19.1/go.mod h1:BK+f9EjjhyAGHoB5HyF2pK2nl49nrsG4aMnhayQ+Huo=
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.3.0-20240213144542-6e830f3fdf19.2 h1:dHSIT1w+0rtb//msVrp/RwrgHIeflbaTMKuF9Lx9HBI=
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.3.0-20240213144542-6e830f3fdf19.2/go.mod h1:EWMdFAEvu6GDt7jvAgoBS/WgXlyDsM7yist2SrcnS3s=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.28.1-20240213144542-6e830f3fdf19.4/go.mod h1:WyRj8OIsAABLNsAELw73BT16v7vvJdEVv771fxX9pJI=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.32.0-20240213144542-6e830f3fdf19.1 h1:YJ13kOhQHoOe4eMd3CqFFeTyQvVBRmeBGLqH/QLuOjQ=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.32.0-20240213144542-6e830f3fdf19.1/go.mod h1:4QGFkgjJ3Wm1EBhQ6tOkaKihV4bFF6DvhTk1r9ZhFOE=
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc-ecosystem/gateway/v2 v2.19.1-20240406062209-1cc152efbf5c.1 h1:gOxy9mc3r7KIWkatjijZwwodJQ9GIyfOdDc0ZewS7FI=
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc-ecosystem/gateway/v2 v2.19.1-20240406062209-1cc152efbf5c.1/go.mod h1:0RBHWuIpAA70W+t5b9EApW4oXHe0oulk34xwsYBETn4=
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.3.0-20240406062209-1cc152efbf5c.2/go.mod h1:ntUHCApEmJb7fi9AoAbe8Ss2IUiivtoypDCbUyAqWE8=
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.3.0-20240406062209-1cc152efbf5c.3 h1:EiautHLlnNmBZdh1wFpmrSDvV4t8sucXGwV6vaE8Xuc=
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.3.0-20240406062209-1cc152efbf5c.3/go.mod h1:4QVX5iWdNcwSFhpXXIXwVH7qT/g9LKvxiqa0SvYJ9hE=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.28.1-20240406062209-1cc152efbf5c.4/go.mod h1:i/s4ALHwKvjA1oGNKpoHg0FpEOTbufoOm/NdTE6YQAE=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.32.0-20240406062209-1cc152efbf5c.1/go.mod h1:IThjyuPqz3nkYBGZMUzrHTQhYDedkvBonqAgLcG7sHc=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.34.0-20240406062209-1cc152efbf5c.1 h1:MCEgqjjzQUUy4T7AXEANwx2ai+qJbGvlnAJEihtiqWs=
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.34.0-20240406062209-1cc152efbf5c.1/go.mod h1:qFzoT6sNuRF9vPeDFmxd9KZ1YgU2vnnno5E5I0OUjOc=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
@@ -63,8 +61,8 @@ cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5
cloud.google.com/go v0.110.9/go.mod h1:rpxevX/0Lqvlbc88b7Sc1SPNdyK1riNBTUU6JXhYNpM=
cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic=
cloud.google.com/go v0.111.0/go.mod h1:0mibmpKP1TyOOFYQY5izo0LnT+ecvOQ0Sg3OdmMiNRU=
cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM=
cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4=
cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM=
cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4=
cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4=
cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw=
cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E=
@@ -81,8 +79,8 @@ cloud.google.com/go/accesscontextmanager v1.8.1/go.mod h1:JFJHfvuaTC+++1iL1coPiG
cloud.google.com/go/accesscontextmanager v1.8.2/go.mod h1:E6/SCRM30elQJ2PKtFMs2YhfJpZSNcJyejhuzoId4Zk=
cloud.google.com/go/accesscontextmanager v1.8.3/go.mod h1:4i/JkF2JiFbhLnnpnfoTX5vRXfhf9ukhU1ANOTALTOQ=
cloud.google.com/go/accesscontextmanager v1.8.4/go.mod h1:ParU+WbMpD34s5JFEnGAnPBYAgUHozaTmDJU7aCU9+M=
cloud.google.com/go/ai v0.3.0 h1:M617N0brv+XFch2KToZUhv6ggzgFZMUnmDkNQjW2pYg=
cloud.google.com/go/ai v0.3.0/go.mod h1:dTuQIBA8Kljuas5z1WNot1QZOl476A9TsFqEi6pzJlI=
cloud.google.com/go/ai v0.3.5-0.20240409161017-ce55ad694f21 h1:kSJt55RNa+qATWnX2xjyq9S2YGDxxBwpmUVZNuFLOi0=
cloud.google.com/go/ai v0.3.5-0.20240409161017-ce55ad694f21/go.mod h1:iX72tmUodGXVDxRDCGUZEPiB9HaMeERXkOdgCkUi8sA=
cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw=
cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY=
cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg=
@@ -99,8 +97,8 @@ cloud.google.com/go/aiplatform v1.52.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv6
cloud.google.com/go/aiplatform v1.54.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv65IHILGA//VEU=
cloud.google.com/go/aiplatform v1.57.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv65IHILGA//VEU=
cloud.google.com/go/aiplatform v1.58.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv65IHILGA//VEU=
cloud.google.com/go/aiplatform v1.59.0 h1:r+P9YStPWrYF52fKyYCQKzTDw4fLiyzLdTEIdxcjmjU=
cloud.google.com/go/aiplatform v1.59.0/go.mod h1:eTlGuHOahHprZw3Hio5VKmtThIOak5/qy6pzdsqcQnM=
cloud.google.com/go/aiplatform v1.60.0 h1:0cSrii1ZeLr16MbBoocyy5KVnrSdiQ3KN/vtrTe7RqE=
cloud.google.com/go/aiplatform v1.60.0/go.mod h1:eTlGuHOahHprZw3Hio5VKmtThIOak5/qy6pzdsqcQnM=
cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI=
cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4=
cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M=
@@ -645,8 +643,8 @@ cloud.google.com/go/iam v1.1.2/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+K
cloud.google.com/go/iam v1.1.3/go.mod h1:3khUlaBXfPKKe7huYgEpDn6FtgRyMEqbkvBxrQyY5SE=
cloud.google.com/go/iam v1.1.4/go.mod h1:l/rg8l1AaA+VFMho/HYx2Vv6xinPSLMF8qfhRPIZ0L8=
cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8=
cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc=
cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI=
cloud.google.com/go/iam v1.1.7 h1:z4VHOhwKLF/+UYXAJDFwGtNF0b6gjsW1Pk9Ml0U/IoM=
cloud.google.com/go/iam v1.1.7/go.mod h1:J4PMPg8TtyurAUvSmPj8FF3EDgY1SPRZxcUGrn7WXGA=
cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc=
cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A=
cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk=
@@ -716,8 +714,8 @@ cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHS
cloud.google.com/go/longrunning v0.5.2/go.mod h1:nqo6DQbNV2pXhGDbDMoN2bWz68MjZUzqv2YttZiveCs=
cloud.google.com/go/longrunning v0.5.3/go.mod h1:y/0ga59EYu58J6SHmmQOvekvND2qODbu8ywBBW7EK7Y=
cloud.google.com/go/longrunning v0.5.4/go.mod h1:zqNVncI0BOP8ST6XQD1+VcvuShMmq7+xFSzOL++V0dI=
cloud.google.com/go/longrunning v0.5.5 h1:GOE6pZFdSrTb4KAiKnXsJBtlE6mEyaW44oKyMILWnOg=
cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s=
cloud.google.com/go/longrunning v0.5.6 h1:xAe8+0YaWoCKr9t1+aWe+OeQgN/iJK1fEgZSXmjuEaE=
cloud.google.com/go/longrunning v0.5.6/go.mod h1:vUaDrWYOMKRuhiv6JBnn49YxCPz2Ayn9GqyjaBT8/mA=
cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE=
cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM=
cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA=
@@ -1075,8 +1073,8 @@ cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi
cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y=
cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4=
cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E=
cloud.google.com/go/storage v1.38.0 h1:Az68ZRGlnNTpIBbLjSMIV2BDcwwXYlRlQzis0llkpJg=
cloud.google.com/go/storage v1.38.0/go.mod h1:tlUADB0mAb9BgYls9lq+8MGkfzOXuLrnHXlpHmvFJoY=
cloud.google.com/go/storage v1.40.0 h1:VEpDQV5CJxFmJ6ueWNsKxcr1QAYOXEgxDa+sBbJahPw=
cloud.google.com/go/storage v1.40.0/go.mod h1:Rrj7/hKlG87BLqDJYtwR0fbPld8uJPbQ2ucUMY7Ir0g=
cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w=
cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I=
cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4=
@@ -1219,10 +1217,10 @@ git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3p
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2 h1:c4k2FIYIh4xtwqrQwV0Ct1v5+ehlNXj5NI/MWVsiTkQ=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2/go.mod h1:5FDJtLEO/GxwNgUxbwrY3LP0pEoThTQJtk2oysdXHxM=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2 h1:FDif4R1+UUR+00q6wquyX90K7A8dN+R5E8GEadoP7sU=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2/go.mod h1:aiYBYui4BJ/BJCAIKs92XiPyQfTaBWqvHujDwKb6CBU=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 h1:LqbJ/WzJUwBf8UiaSzgX7aMclParm9/5Vgp+TY51uBQ=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2/go.mod h1:yInRyqWXAuaPrgI7p70+lDDgh3mlBohis29jGMISnmc=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1 h1:UPeCRD+XY7QlaGQte2EVI2iOcWvUYA2XY8w5T/8v0NQ=
@@ -1232,12 +1230,12 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.2
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.2.1/go.mod h1:Bzf34hhAE9NSxailk8xVeLEZbUjOXcC+GnU1mMKdhLw=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0 h1:AifHbc4mg0x9zW52WOpKbsHaDKuRhlI7TVl47thgQ70=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0/go.mod h1:T5RfihdXtBDxt1Ch2wobif3TvzTdumDy29kahv6AV9A=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.1 h1:fXPMAmuh0gDuRDey0atC8cXBuKIlqCzCkL8sm1n9Ov0=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.1/go.mod h1:SUZc9YRRHfx2+FAQKNDGrssXehqLpxmwRv2mC/5ntj4=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2 h1:YUUxeiOWgdAQE3pXt2H7QXzZs0q8UBjgRbl56qo8GYM=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2/go.mod h1:dmXQgZuiSubAecswZE+Sm8jkvEa7kQgTPVRvwL/nd0E=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
@@ -1328,8 +1326,8 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3d
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.50.34 h1:J1LjHzWNN/yVxQDTr0NIlI5vz9xRPvWiNCjQ4+5wh58=
github.com/aws/aws-sdk-go v1.50.34/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/aws/aws-sdk-go v1.52.3 h1:BNPJmHOXNoM/iBWJKrvaQvJOweRcp3KLpzdb65CfQwU=
github.com/aws/aws-sdk-go v1.52.3/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -1378,10 +1376,8 @@ github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ=
github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM=
github.com/cohere-ai/cohere-go v0.2.0 h1:Gljkn8LTtsAPy79ks1AVmZH9Av4kuQuXEgzEJ/1Ea34=
github.com/cohere-ai/cohere-go v0.2.0/go.mod h1:DFcCu5rwro4wAlluIXY9l17NLGiVBGb2bRio46RXBm8=
github.com/cohere-ai/tokenizer v1.1.1 h1:wCtmCj07O82TMrIiA/CORhIlEYsvMMM8ey+sUdEapHc=
github.com/cohere-ai/tokenizer v1.1.1/go.mod h1:9MNFPd9j1fuiEK3ua2HSCUxxcrfGMlSqpa93livg/C0=
github.com/cohere-ai/cohere-go/v2 v2.7.3 h1:nKr7TpTFbpCMGHWOzWHQc5L/2cEMXQfiGKwf1ofPW2o=
github.com/cohere-ai/cohere-go/v2 v2.7.3/go.mod h1:dlDCT66i8BqZDuuskFvYzsrc+O0M4l5J9Ibckoflvt4=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
@@ -1532,8 +1528,9 @@ github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhO
github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
github.com/go-resty/resty/v2 v2.10.0 h1:Qla4W/+TMmv0fOeeRqzEpXPLfTUnR5HZ1+lGs+CkiCo=
github.com/go-resty/resty/v2 v2.10.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
@@ -1548,11 +1545,13 @@ github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXs
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
@@ -1589,8 +1588,9 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k=
@@ -1600,8 +1600,8 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/generative-ai-go v0.8.0 h1:sbpEC4rdjby19jehqmQ5pF0eXDTGHmNhXuNN+elfC5I=
github.com/google/generative-ai-go v0.8.0/go.mod h1:8fXQk4w+eyTzFokGGJrBFL0/xwXqm3QNhTqOWyX11zs=
github.com/google/generative-ai-go v0.11.0 h1:+wL9xu5jVIgJKC6NmZOxZsBYWDtIap7DGUZ1diQSSnk=
github.com/google/generative-ai-go v0.11.0/go.mod h1:RauvbBjc+AzW0b1LV0VSlxHI5n2i3dz8oJfjboOSiWQ=
github.com/google/gnostic v0.7.0 h1:d7EpuFp8vVdML+y0JJJYiKeOLjKTdH/GvVkLOBWqJpw=
github.com/google/gnostic v0.7.0/go.mod h1:IAcUyMl6vtC95f60EZ8oXyqTsOersP6HbwjeG7EyDPM=
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU=
@@ -1695,8 +1695,8 @@ github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38
github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw=
github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI=
github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
github.com/googleapis/gax-go/v2 v2.12.1 h1:9F8GV9r9ztXyAi00gsMQHNoF51xPZm8uj1dpYt2ZETM=
github.com/googleapis/gax-go/v2 v2.12.1/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc=
github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA=
github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4=
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
@@ -1799,6 +1799,8 @@ github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1
github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw=
github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kedacore/keda/v2 v2.11.2 h1:UgPww0NREqUkM1PGERUz+eb5PlO5oU8V/sT9Hh+ZD60=
github.com/kedacore/keda/v2 v2.11.2/go.mod h1:eutYX+QXTi3QH90F7JvY3tYtV5Jq10o5f56Chk5IVF8=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
@@ -1934,6 +1936,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI=
github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
github.com/oracle/oci-go-sdk/v65 v65.65.1 h1:sv7uD844tJGa2Vc+2KaByoXQ0FllZDGV/2+9MdxN6nA=
github.com/oracle/oci-go-sdk/v65 v65.65.1/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0=
github.com/ovh/go-ovh v1.4.3 h1:Gs3V823zwTFpzgGLZNI6ILS4rmxZgJwJCz54Er9LwD0=
github.com/ovh/go-ovh v1.4.3/go.mod h1:AkPXVtgwB6xlKblMjRKJJmjRp+ogrE7fz2lVgcQY8SY=
github.com/owenrumney/squealer v1.2.1 h1:4ryMMT59aaz8VMsqsD+FDkarADJz0F1dcq2fd0DRR+c=
@@ -2031,8 +2035,8 @@ github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6g
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
github.com/sashabaranov/go-openai v1.20.2 h1:nilzF2EKzaHyK4Rk2Dbu/aJEZbtIvskDIXvfS4yx+6M=
github.com/sashabaranov/go-openai v1.20.2/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
github.com/sashabaranov/go-openai v1.23.0 h1:KYW97r5yc35PI2MxeLZ3OofecB/6H+yxvSNqiT9u8is=
github.com/sashabaranov/go-openai v1.23.0/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.21 h1:yWfiTPwYxB0l5fGMhl/G+liULugVIHD9AU77iNLrURQ=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.21/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
github.com/schollz/progressbar/v3 v3.14.2 h1:EducH6uNLIWsr560zSV1KrTeUb/wZGAHqyMFIEa99ks=
@@ -2050,6 +2054,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ=
github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg=
github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
@@ -2137,27 +2143,29 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0 h1:P+/g8GpuJGYbOp2tAdKrIPUX9JO02q8Q0YNlHolpibA=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0/go.mod h1:tIKj3DbO8N9Y2xo52og3irLsPI4GW02DSMtrVgNMgxg=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 h1:doUP+ExOpH3spVTLS0FcWGLnQrPct/hD/bCPbDRUEAU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0/go.mod h1:rdENBZMT2OE6Ne/KLwpiXudnAsbdrdBaqBvTN8M8BgA=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY=
go.opentelemetry.io/otel v1.23.0 h1:Df0pqjqExIywbMCMTxkAwzjLZtRf+bBKLbUcpxO2C9E=
go.opentelemetry.io/otel v1.23.0/go.mod h1:YCycw9ZeKhcJFrb34iVSkyT0iczq/zYDtZYFufObyB0=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8=
go.opentelemetry.io/otel/metric v1.23.0 h1:pazkx7ss4LFVVYSxYew7L5I6qvLXHA0Ap2pwV+9Cnpo=
go.opentelemetry.io/otel/metric v1.23.0/go.mod h1:MqUW2X2a6Q8RN96E2/nqNoT+z9BSms20Jb7Bbp+HiTo=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw=
go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc=
go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
go.opentelemetry.io/otel/trace v1.23.0 h1:37Ik5Ib7xfYVb4V1UtnT97T1jI+AoIYkJyPkuL4iJgI=
go.opentelemetry.io/otel/trace v1.23.0/go.mod h1:GSGTbIClEsuZrGIzoEHqsVfxgn5UkggkflQwDScNUsk=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.starlark.net v0.0.0-20231016134836-22325403fcb3 h1:CKbpFNZNfaNyEWd6C+F1vLZ0WJjukoU45zDErBmRKPs=
go.starlark.net v0.0.0-20231016134836-22325403fcb3/go.mod h1:LcLNIzVOMp4oV+uusnpk+VU+SzXaJakUuBjoCSWH5dM=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -2186,8 +2194,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -2322,8 +2330,8 @@ golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -2359,8 +2367,8 @@ golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBch
golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0=
golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM=
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -2482,8 +2490,9 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -2503,8 +2512,9 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -2525,8 +2535,9 @@ golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -2688,8 +2699,8 @@ google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvy
google.golang.org/api v0.128.0/go.mod h1:Y611qgqaE92On/7g65MQgxYul3c0rEB894kniWLY750=
google.golang.org/api v0.139.0/go.mod h1:CVagp6Eekz9CjGZ718Z+sloknzkDJE7Vc1Ckj9+viBk=
google.golang.org/api v0.149.0/go.mod h1:Mwn1B7JTXrzXtnvmzQE2BD6bYZQ8DShKZDZbeN9I7qI=
google.golang.org/api v0.167.0 h1:CKHrQD1BLRii6xdkatBDXyKzM0mkawt2QP+H3LtPmSE=
google.golang.org/api v0.167.0/go.mod h1:4FcBc686KFi7QI/U51/2GKKevfZMpM17sCdibqe/bSA=
google.golang.org/api v0.172.0 h1:/1OcMZGPmW1rX2LCu2CmGUD1KXK1+pfzxotxyRUCCdk=
google.golang.org/api v0.172.0/go.mod h1:+fJZq6QXWfa9pXhnIzsjx4yI22d4aI9ZpLb58gvXjis=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -2855,8 +2866,8 @@ google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3/go.mod h1:5RBcpGRx
google.golang.org/genproto v0.0.0-20231212172506-995d672761c0/go.mod h1:l/k7rMz0vFTBPy+tFSGvXEd3z+BcoG1k7EHbqm+YBsY=
google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0=
google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:+Rvu7ElI+aLzyDQhpHMFMMltsD6m7nqpuWDd2CwJw3k=
google.golang.org/genproto v0.0.0-20240205150955-31a09d347014 h1:g/4bk7P6TPMkAUbUhquq98xey1slwvuVJPosdBqYJlU=
google.golang.org/genproto v0.0.0-20240205150955-31a09d347014/go.mod h1:xEgQu1e4stdSSsxPDK8Azkrk/ECl5HvdPf6nbZrTS5M=
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s=
google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8=
google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
@@ -2879,8 +2890,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20231211222908-989df2bf70f3/go.
google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0/go.mod h1:CAny0tYF+0/9rmDB9fahA9YLzX3+AEVl1qXbv5hhj6c=
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0=
google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA=
google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014 h1:x9PwdEgd11LgK+orcck69WVRo7DezSO4VUMPI4xpc8A=
google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014/go.mod h1:rbHMSEDyoYX62nRVLOCc4Qt1HbsdytAYoVwgjiOhF3I=
google.golang.org/genproto/googleapis/api v0.0.0-20240401170217-c3f982113cda h1:b6F6WIV4xHHD0FA4oIyzU6mHWg2WI2X1RBehwa5QN38=
google.golang.org/genproto/googleapis/api v0.0.0-20240401170217-c3f982113cda/go.mod h1:AHcE/gZH76Bk/ROZhQphlRoWo5xKDEtz3eVEO1LfA8c=
google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA=
google.golang.org/genproto/googleapis/bytestream v0.0.0-20230807174057-1744710a1577/go.mod h1:NjCQG/D8JandXxM57PZbAJL1DCNL6EypA0vPPwfsc7c=
google.golang.org/genproto/googleapis/bytestream v0.0.0-20231030173426-d783a09b4405/go.mod h1:GRUCuLdzVqZte8+Dl/D4N25yLzcGqqWaYkeVOwulFqw=
@@ -2907,8 +2918,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 h1:hZB7eLIaYlW9qXRfCq/qDaPdbeY3757uARz5Vvfv+cY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:YUWgXUFRPfoYK1IHMuxH5K6nPEXSCzIMljnQ59lLRCk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa h1:RBgMaUMP+6soRkik4VoN8ojR2nex2TqZwjSSogic+eo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -2960,8 +2971,9 @@ google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9Y
google.golang.org/grpc v1.60.0/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk=
google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/grpc v1.61.2/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@@ -2981,8 +2993,9 @@ google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4=
google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -3043,8 +3056,10 @@ k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/A
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
k8s.io/kubectl v0.28.4 h1:gWpUXW/T7aFne+rchYeHkyB8eVDl5UZce8G4X//kjUQ=
k8s.io/kubectl v0.28.4/go.mod h1:CKOccVx3l+3MmDbkXtIUtibq93nN2hkDR99XDCn7c/c=
k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ=
k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
k8s.io/utils v0.0.0-20240423183400-0849a56e8f22 h1:ao5hUqGhsqdm+bYbjH/pRkCs0unBGe9UyDahzs9zQzQ=
k8s.io/utils v0.0.0-20240423183400-0849a56e8f22/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
knative.dev/pkg v0.0.0-20230616134650-eb63a40adfb0 h1:weQWWxEEbNOPuL4qtGiBZuMSFhcjF/Cu163uktd/xFE=
knative.dev/pkg v0.0.0-20230616134650-eb63a40adfb0/go.mod h1:dqC6IrvyBE7E+oZocs5PkVhq1G59pDTA7r8U17EAKMk=
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=

View File

@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"os"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
@@ -76,6 +77,9 @@ func GetModelOrDefault(model string) string {
// GetModelOrDefault check config region
func GetRegionOrDefault(region string) string {
if os.Getenv("AWS_DEFAULT_REGION") != "" {
region = os.Getenv("AWS_DEFAULT_REGION")
}
// Check if the provided model is in the list
for _, m := range BEDROCKER_SUPPORTED_REGION {
if m == region {

View File

@@ -33,6 +33,7 @@ type SageMakerAIClient struct {
temperature float32
endpoint string
topP float32
topK int32
maxTokens int
}
@@ -56,6 +57,7 @@ type Message struct {
type Parameters struct {
MaxNewTokens int `json:"max_new_tokens"`
TopP float64 `json:"top_p"`
TopK float64 `json:"top_k"`
Temperature float64 `json:"temperature"`
}
@@ -74,6 +76,7 @@ func (c *SageMakerAIClient) Configure(config IAIConfig) error {
c.temperature = config.GetTemperature()
c.maxTokens = config.GetMaxTokens()
c.topP = config.GetTopP()
c.topK = config.GetTopK()
return nil
}
@@ -90,6 +93,7 @@ func (c *SageMakerAIClient) GetCompletion(_ context.Context, prompt string) (str
Parameters: Parameters{
MaxNewTokens: int(c.maxTokens),
TopP: float64(c.topP),
TopK: float64(c.topK),
Temperature: float64(c.temperature),
},
}

View File

@@ -34,7 +34,7 @@ func (c *AzureAIClient) Configure(config IAIConfig) error {
return azureModelMapping[model]
}
if proxyEndpoint != "" {
proxyUrl, err := url.Parse(proxyEndpoint)
if err != nil {

View File

@@ -17,7 +17,9 @@ import (
"context"
"errors"
"github.com/cohere-ai/cohere-go"
api "github.com/cohere-ai/cohere-go/v2"
cohere "github.com/cohere-ai/cohere-go/v2/client"
"github.com/cohere-ai/cohere-go/v2/option"
)
const cohereAIClientName = "cohere"
@@ -28,45 +30,49 @@ type CohereClient struct {
client *cohere.Client
model string
temperature float32
maxTokens int
}
func (c *CohereClient) Configure(config IAIConfig) error {
token := config.GetPassword()
client, err := cohere.CreateClient(token)
if err != nil {
return err
opts := []option.RequestOption{
cohere.WithToken(token),
}
baseURL := config.GetBaseURL()
if baseURL != "" {
client.BaseURL = baseURL
opts = append(opts, cohere.WithBaseURL(baseURL))
}
client := cohere.NewClient(opts...)
if client == nil {
return errors.New("error creating Cohere client")
}
c.client = client
c.model = config.GetModel()
c.temperature = config.GetTemperature()
c.maxTokens = config.GetMaxTokens()
return nil
}
func (c *CohereClient) GetCompletion(_ context.Context, prompt string) (string, error) {
func (c *CohereClient) GetCompletion(ctx context.Context, prompt string) (string, error) {
// Create a completion request
resp, err := c.client.Generate(cohere.GenerateOptions{
Model: c.model,
Prompt: prompt,
MaxTokens: cohere.Uint(2048),
Temperature: cohere.Float64(float64(c.temperature)),
K: cohere.Int(0),
StopSequences: []string{},
ReturnLikelihoods: "NONE",
response, err := c.client.Chat(ctx, &api.ChatRequest{
Message: prompt,
Model: &c.model,
K: api.Int(0),
Preamble: api.String(""),
Temperature: api.Float64(float64(c.temperature)),
RawPrompting: api.Bool(false),
MaxTokens: api.Int(c.maxTokens),
})
if err != nil {
return "", err
}
return resp.Generations[0].Text, nil
return response.Text, nil
}
func (c *CohereClient) GetName() string {

View File

@@ -31,6 +31,7 @@ type GoogleGenAIClient struct {
model string
temperature float32
topP float32
topK int32
maxTokens int
}
@@ -53,6 +54,7 @@ func (c *GoogleGenAIClient) Configure(config IAIConfig) error {
c.model = config.GetModel()
c.temperature = config.GetTemperature()
c.topP = config.GetTopP()
c.topK = config.GetTopK()
c.maxTokens = config.GetMaxTokens()
return nil
}
@@ -62,6 +64,7 @@ func (c *GoogleGenAIClient) GetCompletion(ctx context.Context, prompt string) (s
model := c.client.GenerativeModel(c.model)
model.SetTemperature(c.temperature)
model.SetTopP(c.topP)
model.SetTopK(c.topK)
model.SetMaxOutputTokens(int32(c.maxTokens))
// Google AI SDK is capable of different inputs than just text, for now set explicit text prompt type.

View File

@@ -30,6 +30,7 @@ type GoogleVertexAIClient struct {
model string
temperature float32
topP float32
topK int32
maxTokens int
}
@@ -111,6 +112,7 @@ func (g *GoogleVertexAIClient) Configure(config IAIConfig) error {
g.model = GetVertexAIModelOrDefault(config.GetModel())
g.temperature = config.GetTemperature()
g.topP = config.GetTopP()
g.topK = config.GetTopK()
g.maxTokens = config.GetMaxTokens()
return nil
@@ -121,6 +123,7 @@ func (g *GoogleVertexAIClient) GetCompletion(ctx context.Context, prompt string)
model := g.client.GenerativeModel(g.model)
model.SetTemperature(g.temperature)
model.SetTopP(g.topP)
model.SetTopK(float32(g.topK))
model.SetMaxOutputTokens(int32(g.maxTokens))
// Google AI SDK is capable of different inputs than just text, for now set explicit text prompt type.

View File

@@ -2,6 +2,7 @@ package ai
import (
"context"
"github.com/hupe1980/go-huggingface"
"k8s.io/utils/ptr"
)
@@ -14,6 +15,7 @@ type HuggingfaceClient struct {
client *huggingface.InferenceClient
model string
topP float32
topK int32
temperature float32
maxTokens int
}
@@ -26,6 +28,7 @@ func (c *HuggingfaceClient) Configure(config IAIConfig) error {
c.client = client
c.model = config.GetModel()
c.topP = config.GetTopP()
c.topK = config.GetTopK()
c.temperature = config.GetTemperature()
if config.GetMaxTokens() > 500 {
c.maxTokens = 500
@@ -43,6 +46,7 @@ func (c *HuggingfaceClient) GetCompletion(ctx context.Context, prompt string) (s
Model: c.model,
Parameters: huggingface.ConversationalParameters{
TopP: ptr.To[float64](float64(c.topP)),
TopK: ptr.To[int](int(c.topK)),
Temperature: ptr.To[float64](float64(c.temperature)),
MaxLength: &c.maxTokens,
},

View File

@@ -29,6 +29,7 @@ var (
&GoogleGenAIClient{},
&HuggingfaceClient{},
&GoogleVertexAIClient{},
&OCIGenAIClient{},
}
Backends = []string{
openAIClientName,
@@ -41,6 +42,7 @@ var (
noopAIClientName,
huggingfaceAIClientName,
googleVertexAIClientName,
ociClientName,
}
)
@@ -72,8 +74,10 @@ type IAIConfig interface {
GetTemperature() float32
GetProviderRegion() string
GetTopP() float32
GetTopK() int32
GetMaxTokens() int
GetProviderId() string
GetCompartmentId() string
}
func NewClient(provider string) IAI {
@@ -103,7 +107,9 @@ type AIProvider struct {
Temperature float32 `mapstructure:"temperature" yaml:"temperature,omitempty"`
ProviderRegion string `mapstructure:"providerregion" yaml:"providerregion,omitempty"`
ProviderId string `mapstructure:"providerid" yaml:"providerid,omitempty"`
CompartmentId string `mapstructure:"compartmentid" yaml:"compartmentid,omitempty"`
TopP float32 `mapstructure:"topp" yaml:"topp,omitempty"`
TopK int32 `mapstructure:"topk" yaml:"topk,omitempty"`
MaxTokens int `mapstructure:"maxtokens" yaml:"maxtokens,omitempty"`
}
@@ -123,6 +129,10 @@ func (p *AIProvider) GetTopP() float32 {
return p.TopP
}
func (p *AIProvider) GetTopK() int32 {
return p.TopK
}
func (p *AIProvider) GetMaxTokens() int {
return p.MaxTokens
}
@@ -150,7 +160,11 @@ func (p *AIProvider) GetProviderId() string {
return p.ProviderId
}
var passwordlessProviders = []string{"localai", "amazonsagemaker", "amazonbedrock", "googlevertexai"}
func (p *AIProvider) GetCompartmentId() string {
return p.CompartmentId
}
var passwordlessProviders = []string{"localai", "amazonsagemaker", "amazonbedrock", "googlevertexai", "oci"}
func NeedPassword(backend string) bool {
for _, b := range passwordlessProviders {

97
pkg/ai/ocigenai.go Normal file
View File

@@ -0,0 +1,97 @@
/*
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 ai
import (
"context"
"errors"
"github.com/oracle/oci-go-sdk/v65/common"
"github.com/oracle/oci-go-sdk/v65/generativeaiinference"
"strings"
)
const ociClientName = "oci"
type OCIGenAIClient struct {
nopCloser
client *generativeaiinference.GenerativeAiInferenceClient
model string
compartmentId string
temperature float32
topP float32
maxTokens int
}
func (c *OCIGenAIClient) GetName() string {
return ociClientName
}
func (c *OCIGenAIClient) Configure(config IAIConfig) error {
config.GetEndpointName()
c.model = config.GetModel()
c.temperature = config.GetTemperature()
c.topP = config.GetTopP()
c.maxTokens = config.GetMaxTokens()
c.compartmentId = config.GetCompartmentId()
provider := common.DefaultConfigProvider()
client, err := generativeaiinference.NewGenerativeAiInferenceClientWithConfigurationProvider(provider)
if err != nil {
return err
}
c.client = &client
return nil
}
func (c *OCIGenAIClient) GetCompletion(ctx context.Context, prompt string) (string, error) {
generateTextRequest := c.newGenerateTextRequest(prompt)
generateTextResponse, err := c.client.GenerateText(ctx, generateTextRequest)
if err != nil {
return "", err
}
return extractGeneratedText(generateTextResponse.InferenceResponse)
}
func (c *OCIGenAIClient) newGenerateTextRequest(prompt string) generativeaiinference.GenerateTextRequest {
temperatureF64 := float64(c.temperature)
topPF64 := float64(c.topP)
return generativeaiinference.GenerateTextRequest{
GenerateTextDetails: generativeaiinference.GenerateTextDetails{
CompartmentId: &c.compartmentId,
ServingMode: generativeaiinference.OnDemandServingMode{
ModelId: &c.model,
},
InferenceRequest: generativeaiinference.CohereLlmInferenceRequest{
Prompt: &prompt,
MaxTokens: &c.maxTokens,
Temperature: &temperatureF64,
TopP: &topPF64,
},
},
}
}
func extractGeneratedText(llmInferenceResponse generativeaiinference.LlmInferenceResponse) (string, error) {
response, ok := llmInferenceResponse.(generativeaiinference.CohereLlmInferenceResponse)
if !ok {
return "", errors.New("failed to extract generated text from backed response")
}
sb := strings.Builder{}
for _, text := range response.GeneratedTexts {
if text.Text != nil {
sb.WriteString(*text.Text)
}
}
return sb.String(), nil
}

View File

@@ -30,6 +30,7 @@ type OpenAIClient struct {
client *openai.Client
model string
temperature float32
topP float32
}
const (
@@ -37,7 +38,6 @@ const (
maxToken = 2048
presencePenalty = 0.0
frequencyPenalty = 0.0
topP = 1.0
)
func (c *OpenAIClient) Configure(config IAIConfig) error {
@@ -63,7 +63,7 @@ func (c *OpenAIClient) Configure(config IAIConfig) error {
Transport: transport,
}
}
client := openai.NewClientWithConfig(defaultConfig)
if client == nil {
return errors.New("error creating OpenAI client")
@@ -71,6 +71,7 @@ func (c *OpenAIClient) Configure(config IAIConfig) error {
c.client = client
c.model = config.GetModel()
c.temperature = config.GetTemperature()
c.topP = config.GetTopP()
return nil
}
@@ -88,7 +89,7 @@ func (c *OpenAIClient) GetCompletion(ctx context.Context, prompt string) (string
MaxTokens: maxToken,
PresencePenalty: presencePenalty,
FrequencyPenalty: frequencyPenalty,
TopP: topP,
TopP: c.topP,
})
if err != nil {
return "", err

View File

@@ -17,6 +17,11 @@ import (
"context"
"encoding/json"
"fmt"
"strings"
"testing"
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/magiconair/properties/assert"
@@ -26,8 +31,6 @@ import (
networkingv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
"strings"
"testing"
)
// sub-function
@@ -85,6 +88,7 @@ func analysis_RunAnalysisFilterTester(t *testing.T, filterFlag string) []common.
Client: &kubernetes.Client{
Client: clientset,
},
WithDoc: true,
}
if len(filterFlag) > 0 {
// `--filter` is explicitly given
@@ -133,6 +137,10 @@ func TestAnalysis_RunAnalysisActiveFilter(t *testing.T) {
viper.SetDefault("active_filters", []string{"Ingress", "Service", "Pod"})
results = analysis_RunAnalysisFilterTester(t, "")
assert.Equal(t, len(results), 3)
// Invalid filter
results = analysis_RunAnalysisFilterTester(t, "invalid")
assert.Equal(t, len(results), 0)
}
func TestAnalysis_NoProblemJsonOutput(t *testing.T) {
@@ -279,3 +287,120 @@ func TestAnalysis_MultipleProblemJsonOutput(t *testing.T) {
require.Equal(t, got, expected)
}
func TestNewAnalysis(t *testing.T) {
disabledCache := cache.New("disabled-cache")
disabledCache.DisableCache()
aiClient := &ai.NoOpAIClient{}
results := []common.Result{
{
Kind: "VulnerabilityReport",
Error: []common.Failure{
{
Text: "This is a custom failure",
KubernetesDoc: "test-kubernetes-doc",
Sensitive: []common.Sensitive{
{
Masked: "masked-error",
Unmasked: "unmasked-error",
},
},
},
},
},
}
tests := []struct {
name string
a Analysis
output string
anonymize bool
expectedErr string
}{
{
name: "Empty results",
a: Analysis{},
},
{
name: "cache disabled",
a: Analysis{
AIClient: aiClient,
Cache: disabledCache,
Results: results,
},
},
{
name: "output and anonymize both set",
a: Analysis{
AIClient: aiClient,
Cache: cache.New("test-cache"),
Results: results,
},
output: "test-output",
anonymize: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
err := tt.a.GetAIResults(tt.output, tt.anonymize)
if tt.expectedErr == "" {
require.NoError(t, err)
} else {
require.ErrorContains(t, err, tt.expectedErr)
}
})
}
}
func TestGetAIResultForSanitizedFailures(t *testing.T) {
enabledCache := cache.New("enabled-cache")
disabledCache := cache.New("disabled-cache")
disabledCache.DisableCache()
aiClient := &ai.NoOpAIClient{}
tests := []struct {
name string
a Analysis
texts []string
promptTmpl string
expectedOutput string
expectedErr string
}{
{
name: "Cache enabled",
a: Analysis{
AIClient: aiClient,
Cache: enabledCache,
},
texts: []string{"some-data"},
expectedOutput: "I am a noop response to the prompt %!(EXTRA string=, string=some-data)",
},
{
name: "cache disabled",
a: Analysis{
AIClient: aiClient,
Cache: disabledCache,
Language: "English",
},
texts: []string{"test input"},
promptTmpl: "Response in %s: %s",
expectedOutput: "I am a noop response to the prompt Response in English: test input",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
output, err := tt.a.getAIResultForSanitizedFailures(tt.texts, tt.promptTmpl)
if tt.expectedErr == "" {
require.NoError(t, err)
require.Equal(t, tt.expectedOutput, output)
} else {
require.ErrorContains(t, err, tt.expectedErr)
require.Empty(t, output)
}
})
}
}

View File

@@ -78,8 +78,10 @@ func (a *Analysis) textOutput() ([]byte, error) {
return []byte(output.String()), nil
}
for n, result := range a.Results {
output.WriteString(fmt.Sprintf("%s %s(%s)\n", color.CyanString("%d", n),
color.YellowString(result.Name), color.CyanString(result.ParentObject)))
output.WriteString(fmt.Sprintf("%s: %s %s(%s)\n", color.CyanString("%d", n),
color.HiYellowString(result.Kind),
color.YellowString(result.Name),
color.CyanString(result.ParentObject)))
for _, err := range result.Error {
output.WriteString(fmt.Sprintf("- %s %s\n", color.RedString("Error:"), color.RedString(err.Text)))
if err.KubernetesDoc != "" {

View File

@@ -0,0 +1,64 @@
/*
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 analysis
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestPrintOutput(t *testing.T) {
require.NotEmpty(t, getOutputFormats())
tests := []struct {
name string
a *Analysis
format string
expectedOutput string
expectedErr string
}{
{
name: "json format",
a: &Analysis{},
format: "json",
expectedOutput: "{\n \"provider\": \"\",\n \"errors\": null,\n \"status\": \"OK\",\n \"problems\": 0,\n \"results\": null\n}",
},
{
name: "text format",
a: &Analysis{},
format: "text",
expectedOutput: "AI Provider: AI not used; --explain not set\n\nNo problems detected\n",
},
{
name: "unsupported format",
a: &Analysis{},
format: "unsupported",
expectedErr: "unsupported output format: unsupported. Available format",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
output, err := tt.a.PrintOutput(tt.format)
if tt.expectedErr == "" {
require.NoError(t, err)
require.Contains(t, string(output), tt.expectedOutput)
} else {
require.ErrorContains(t, err, tt.expectedErr)
require.Nil(t, output)
}
})
}
}

View File

@@ -123,15 +123,15 @@ func (analyzer CronJobAnalyzer) Analyze(a common.Analyzer) ([]common.Result, err
AnalyzerErrorsMetric.WithLabelValues(kind, cronJob.Name, cronJob.Namespace).Set(float64(len(failures)))
}
}
for key, value := range preAnalysis {
currentAnalysis := common.Result{
Kind: kind,
Name: key,
Error: value.FailureDetails,
}
a.Results = append(a.Results, currentAnalysis)
for key, value := range preAnalysis {
currentAnalysis := common.Result{
Kind: kind,
Name: key,
Error: value.FailureDetails,
}
a.Results = append(a.Results, currentAnalysis)
}
return a.Results, nil

View File

@@ -15,219 +15,144 @@ 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"
batchv1 "k8s.io/api/batch/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
)
func TestCronJobSuccess(t *testing.T) {
clientset := fake.NewSimpleClientset(&batchv1.CronJob{
ObjectMeta: metav1.ObjectMeta{
Name: "example-cronjob",
Namespace: "default",
Annotations: map[string]string{
"analysisDate": "2022-04-01",
},
Labels: map[string]string{
"app": "example-app",
},
},
Spec: batchv1.CronJobSpec{
Schedule: "*/1 * * * *",
ConcurrencyPolicy: "Allow",
JobTemplate: batchv1.JobTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"app": "example-app",
},
},
Spec: batchv1.JobSpec{
Template: v1.PodTemplateSpec{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "example-container",
Image: "nginx",
},
},
RestartPolicy: v1.RestartPolicyOnFailure,
},
},
},
},
},
})
func TestCronJobAnalyzer(t *testing.T) {
suspend := new(bool)
*suspend = true
invalidStartingDeadline := new(int64)
*invalidStartingDeadline = -7
validStartingDeadline := new(int64)
*validStartingDeadline = 7
config := common.Analyzer{
Client: &kubernetes.Client{
Client: clientset,
},
Context: context.Background(),
Namespace: "default",
}
analyzer := CronJobAnalyzer{}
analysisResults, err := analyzer.Analyze(config)
if err != nil {
t.Error(err)
}
assert.Equal(t, len(analysisResults), 0)
}
func TestCronJobBroken(t *testing.T) {
clientset := fake.NewSimpleClientset(&batchv1.CronJob{
ObjectMeta: metav1.ObjectMeta{
Name: "example-cronjob",
Namespace: "default",
Annotations: map[string]string{
"analysisDate": "2022-04-01",
},
Labels: map[string]string{
"app": "example-app",
},
},
Spec: batchv1.CronJobSpec{
Schedule: "*** * * * *",
ConcurrencyPolicy: "Allow",
JobTemplate: batchv1.JobTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"app": "example-app",
},
},
Spec: batchv1.JobSpec{
Template: v1.PodTemplateSpec{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "example-container",
Image: "nginx",
},
},
RestartPolicy: v1.RestartPolicyOnFailure,
},
},
},
},
},
})
config := common.Analyzer{
Client: &kubernetes.Client{
Client: clientset,
},
Context: context.Background(),
Namespace: "default",
}
analyzer := CronJobAnalyzer{}
analysisResults, err := analyzer.Analyze(config)
if err != nil {
t.Error(err)
}
assert.Equal(t, len(analysisResults), 1)
assert.Equal(t, analysisResults[0].Name, "default/example-cronjob")
assert.Equal(t, analysisResults[0].Kind, "CronJob")
}
func TestCronJobBrokenMultipleNamespaceFiltering(t *testing.T) {
clientset := fake.NewSimpleClientset(
&batchv1.CronJob{
ObjectMeta: metav1.ObjectMeta{
Name: "example-cronjob",
Namespace: "default",
Annotations: map[string]string{
"analysisDate": "2022-04-01",
},
Labels: map[string]string{
"app": "example-app",
},
},
Spec: batchv1.CronJobSpec{
Schedule: "*** * * * *",
ConcurrencyPolicy: "Allow",
JobTemplate: batchv1.JobTemplateSpec{
Client: fake.NewSimpleClientset(
&batchv1.CronJob{
ObjectMeta: metav1.ObjectMeta{
Name: "CJ1",
// This CronJob won't be list because of namespace filtering.
Namespace: "test",
},
},
&batchv1.CronJob{
ObjectMeta: metav1.ObjectMeta{
Name: "CJ2",
Namespace: "default",
},
// A suspended CronJob will contribute to failures.
Spec: batchv1.CronJobSpec{
Suspend: suspend,
},
},
&batchv1.CronJob{
ObjectMeta: metav1.ObjectMeta{
Name: "CJ3",
Namespace: "default",
},
Spec: batchv1.CronJobSpec{
// Valid schedule
Schedule: "*/1 * * * *",
// Negative starting deadline
StartingDeadlineSeconds: invalidStartingDeadline,
},
},
&batchv1.CronJob{
ObjectMeta: metav1.ObjectMeta{
Name: "CJ4",
Namespace: "default",
},
Spec: batchv1.CronJobSpec{
// Invalid schedule
Schedule: "*** * * * *",
},
},
&batchv1.CronJob{
ObjectMeta: metav1.ObjectMeta{
Name: "CJ5",
Namespace: "default",
},
Spec: batchv1.CronJobSpec{
// Valid schedule
Schedule: "*/1 * * * *",
// Positive starting deadline shouldn't be any problem.
StartingDeadlineSeconds: validStartingDeadline,
},
},
&batchv1.CronJob{
// This cronjob shouldn't contribute to any failures.
ObjectMeta: metav1.ObjectMeta{
Name: "successful-cronjob",
Namespace: "default",
Annotations: map[string]string{
"analysisDate": "2022-04-01",
},
Labels: map[string]string{
"app": "example-app",
},
},
Spec: batchv1.JobSpec{
Template: v1.PodTemplateSpec{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "example-container",
Image: "nginx",
Spec: batchv1.CronJobSpec{
Schedule: "*/1 * * * *",
ConcurrencyPolicy: "Allow",
JobTemplate: batchv1.JobTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"app": "example-app",
},
},
Spec: batchv1.JobSpec{
Template: v1.PodTemplateSpec{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "example-container",
Image: "nginx",
},
},
RestartPolicy: v1.RestartPolicyOnFailure,
},
},
RestartPolicy: v1.RestartPolicyOnFailure,
},
},
},
},
},
},
&batchv1.CronJob{
ObjectMeta: metav1.ObjectMeta{
Name: "example-cronjob",
Namespace: "other-namespace",
Annotations: map[string]string{
"analysisDate": "2022-04-01",
},
Labels: map[string]string{
"app": "example-app",
},
},
Spec: batchv1.CronJobSpec{
Schedule: "*** * * * *",
ConcurrencyPolicy: "Allow",
JobTemplate: batchv1.JobTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"app": "example-app",
},
},
Spec: batchv1.JobSpec{
Template: v1.PodTemplateSpec{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "example-container",
Image: "nginx",
},
},
RestartPolicy: v1.RestartPolicyOnFailure,
},
},
},
},
},
})
config := common.Analyzer{
Client: &kubernetes.Client{
Client: clientset,
),
},
Context: context.Background(),
Namespace: "default",
}
analyzer := CronJobAnalyzer{}
analysisResults, err := analyzer.Analyze(config)
if err != nil {
t.Error(err)
cjAnalyzer := CronJobAnalyzer{}
results, err := cjAnalyzer.Analyze(config)
require.NoError(t, err)
sort.Slice(results, func(i, j int) bool {
return results[i].Name < results[j].Name
})
expectations := []string{
"default/CJ2",
"default/CJ3",
"default/CJ4",
}
assert.Equal(t, len(analysisResults), 1)
assert.Equal(t, analysisResults[0].Name, "default/example-cronjob")
assert.Equal(t, analysisResults[0].Kind, "CronJob")
require.Equal(t, len(expectations), len(results))
for i, result := range results {
require.Equal(t, expectations[i], result.Name)
}
}

View File

@@ -1,50 +0,0 @@
/*
Copyright 2023 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package analyzer
import (
"context"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func FetchLatestEvent(ctx context.Context, kubernetesClient *kubernetes.Client, namespace string, name string) (*v1.Event, error) {
// get the list of events
events, err := kubernetesClient.GetClient().CoreV1().Events(namespace).List(ctx,
metav1.ListOptions{
FieldSelector: "involvedObject.name=" + name,
})
if err != nil {
return nil, err
}
// find most recent event
var latestEvent *v1.Event
for _, event := range events.Items {
if latestEvent == nil {
// this is required, as a pointer to a loop variable would always yield the latest value in the range
e := event
latestEvent = &e
}
if event.LastTimestamp.After(latestEvent.LastTimestamp.Time) {
// this is required, as a pointer to a loop variable would always yield the latest value in the range
e := event
latestEvent = &e
}
}
return latestEvent, nil
}

View File

@@ -140,8 +140,10 @@ func (HpaAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
Error: value.FailureDetails,
}
parent, _ := util.GetParent(a.Client, value.HorizontalPodAutoscalers.ObjectMeta)
currentAnalysis.ParentObject = parent
parent, found := util.GetParent(a.Client, value.HorizontalPodAutoscalers.ObjectMeta)
if found {
currentAnalysis.ParentObject = parent
}
a.Results = append(a.Results, currentAnalysis)
}

View File

@@ -163,8 +163,10 @@ func (IngressAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
Error: value.FailureDetails,
}
parent, _ := util.GetParent(a.Client, value.Ingress.ObjectMeta)
currentAnalysis.ParentObject = parent
parent, found := util.GetParent(a.Client, value.Ingress.ObjectMeta)
if found {
currentAnalysis.ParentObject = parent
}
a.Results = append(a.Results, currentAnalysis)
}

View File

@@ -15,146 +15,189 @@ package analyzer
import (
"context"
"strings"
"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"
networkingv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
)
func TestIngressAnalyzer(t *testing.T) {
clientset := fake.NewSimpleClientset(
&networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "example",
Namespace: "default",
Annotations: map[string]string{},
validIgClassName := new(string)
*validIgClassName = "valid-ingress-class"
var igRule networkingv1.IngressRule
httpRule := networkingv1.HTTPIngressRuleValue{
Paths: []networkingv1.HTTPIngressPath{
{
Path: "/",
Backend: networkingv1.IngressBackend{
Service: &networkingv1.IngressServiceBackend{
// This service exists.
Name: "Service1",
},
},
},
})
ingressAnalyzer := IngressAnalyzer{}
{
Path: "/test1",
Backend: networkingv1.IngressBackend{
Service: &networkingv1.IngressServiceBackend{
// This service is in the test namespace
// Hence, it won't be discovered.
Name: "Service2",
},
},
},
{
Path: "/test2",
Backend: networkingv1.IngressBackend{
Service: &networkingv1.IngressServiceBackend{
// This service doesn't exist.
Name: "Service3",
},
},
},
},
}
igRule.IngressRuleValue.HTTP = &httpRule
config := common.Analyzer{
Client: &kubernetes.Client{
Client: clientset,
},
Context: context.Background(),
Namespace: "default",
}
analysisResults, err := ingressAnalyzer.Analyze(config)
if err != nil {
t.Error(err)
}
assert.Equal(t, len(analysisResults), 1)
}
Client: fake.NewSimpleClientset(
&networkingv1.Ingress{
// Doesn't specify an ingress class.
ObjectMeta: metav1.ObjectMeta{
Name: "Ingress1",
Namespace: "default",
},
},
&networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "Ingress2",
Namespace: "default",
// Specify an invalid ingress class name using annotations.
Annotations: map[string]string{
"kubernetes.io/ingress.class": "invalid-class",
},
},
},
&networkingv1.Ingress{
// Namespace filtering.
ObjectMeta: metav1.ObjectMeta{
Name: "Ingress3",
Namespace: "test",
},
},
&networkingv1.IngressClass{
ObjectMeta: metav1.ObjectMeta{
Name: *validIgClassName,
},
},
&networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "Ingress4",
Namespace: "default",
// Specify valid ingress class name using annotations.
Annotations: map[string]string{
"kubernetes.io/ingress.class": *validIgClassName,
},
},
},
&v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "Service1",
Namespace: "default",
},
},
&v1.Service{
ObjectMeta: metav1.ObjectMeta{
// Namespace filtering.
Name: "Service2",
Namespace: "test",
},
},
&v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "Secret1",
Namespace: "default",
},
},
&v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "Secret2",
Namespace: "test",
},
},
&networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "Ingress5",
Namespace: "default",
},
func TestIngressAnalyzerWithMultipleIngresses(t *testing.T) {
clientset := fake.NewSimpleClientset(
&networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "example",
Namespace: "default",
Annotations: map[string]string{},
},
},
&networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "example-2",
Namespace: "default",
Annotations: map[string]string{},
},
},
)
ingressAnalyzer := IngressAnalyzer{}
config := common.Analyzer{
Client: &kubernetes.Client{
Client: clientset,
// Specify valid ingress class name in spec.
Spec: networkingv1.IngressSpec{
IngressClassName: validIgClassName,
Rules: []networkingv1.IngressRule{
igRule,
},
TLS: []networkingv1.IngressTLS{
{
// This won't contribute to any failures.
SecretName: "Secret1",
},
{
// This secret won't be discovered because of namespace filtering.
SecretName: "Secret2",
},
{
// This secret doesn't exist.
SecretName: "Secret3",
},
},
},
},
),
},
Context: context.Background(),
Namespace: "default",
}
analysisResults, err := ingressAnalyzer.Analyze(config)
if err != nil {
t.Error(err)
}
assert.Equal(t, len(analysisResults), 2)
}
igAnalyzer := IngressAnalyzer{}
results, err := igAnalyzer.Analyze(config)
require.NoError(t, err)
func TestIngressAnalyzerWithoutIngressClassAnnotation(t *testing.T) {
sort.Slice(results, func(i, j int) bool {
return results[i].Name < results[j].Name
})
clientset := fake.NewSimpleClientset(
&networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "example",
Namespace: "default",
Annotations: map[string]string{},
},
})
ingressAnalyzer := IngressAnalyzer{}
config := common.Analyzer{
Client: &kubernetes.Client{
Client: clientset,
expectations := []struct {
name string
failuresCount int
}{
{
name: "default/Ingress1",
failuresCount: 1,
},
{
name: "default/Ingress2",
failuresCount: 1,
},
{
name: "default/Ingress5",
failuresCount: 4,
},
Context: context.Background(),
Namespace: "default",
}
analysisResults, err := ingressAnalyzer.Analyze(config)
if err != nil {
t.Error(err)
}
require.Equal(t, len(expectations), len(results))
var errorFound bool
for _, analysis := range analysisResults {
for _, err := range analysis.Error {
if strings.Contains(err.Text, "does not specify an Ingress class") {
errorFound = true
break
}
}
if errorFound {
break
}
}
if !errorFound {
t.Error("expected error 'does not specify an Ingress class' not found in analysis results")
for i, result := range results {
require.Equal(t, expectations[i].name, result.Name)
require.Equal(t, expectations[i].failuresCount, len(result.Error))
}
}
func TestIngressAnalyzerNamespaceFiltering(t *testing.T) {
clientset := fake.NewSimpleClientset(
&networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "example",
Namespace: "default",
Annotations: map[string]string{},
},
},
&networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "example",
Namespace: "other-namespace",
Annotations: map[string]string{},
},
})
ingressAnalyzer := IngressAnalyzer{}
config := common.Analyzer{
Client: &kubernetes.Client{
Client: clientset,
},
Context: context.Background(),
Namespace: "default",
}
analysisResults, err := ingressAnalyzer.Analyze(config)
if err != nil {
t.Error(err)
}
assert.Equal(t, len(analysisResults), 1)
}

View File

@@ -71,7 +71,7 @@ func (LogAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
rawlogs := string(podLogs)
if errorPattern.MatchString(strings.ToLower(rawlogs)) {
failures = append(failures, common.Failure{
Text: printErrorLines(pod.Name, pod.Namespace, rawlogs, errorPattern),
Text: printErrorLines(rawlogs, errorPattern),
Sensitive: []common.Sensitive{
{
Unmasked: pod.Name,
@@ -96,14 +96,16 @@ func (LogAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
Name: key,
Error: value.FailureDetails,
}
parent, _ := util.GetParent(a.Client, value.Pod.ObjectMeta)
currentAnalysis.ParentObject = parent
parent, found := util.GetParent(a.Client, value.Pod.ObjectMeta)
if found {
currentAnalysis.ParentObject = parent
}
a.Results = append(a.Results, currentAnalysis)
}
return a.Results, nil
}
func printErrorLines(podName, namespace, logs string, errorPattern *regexp.Regexp) string {
func printErrorLines(logs string, errorPattern *regexp.Regexp) string {
// Split the logs into lines
logLines := strings.Split(logs, "\n")

120
pkg/analyzer/log_test.go Normal file
View File

@@ -0,0 +1,120 @@
/*
Copyright 2023 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package analyzer
import (
"context"
"regexp"
"sort"
"testing"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"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"
)
func TestLogAnalyzer(t *testing.T) {
oldPattern := errorPattern
errorPattern = regexp.MustCompile(`(fake logs)`)
t.Cleanup(func() {
errorPattern = oldPattern
})
config := common.Analyzer{
Client: &kubernetes.Client{
Client: fake.NewSimpleClientset(
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "Pod1",
Namespace: "default",
Labels: map[string]string{
"Name": "Pod1",
"Namespace": "default",
},
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "test-container1",
},
{
Name: "test-container2",
},
},
},
},
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "Pod2",
Namespace: "default",
Labels: map[string]string{
"Name": "Pod1",
"Namespace": "default",
},
},
},
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "Pod3",
Namespace: "test-namespace",
Labels: map[string]string{
"Name": "Pod1",
"Namespace": "test-namespace",
},
},
},
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "Pod4",
Namespace: "default",
Labels: map[string]string{
"Name": "Pod4",
"Namespace": "default",
},
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "test-container3",
},
},
},
},
),
},
Context: context.Background(),
Namespace: "default",
}
logAnalyzer := LogAnalyzer{}
results, err := logAnalyzer.Analyze(config)
require.NoError(t, err)
sort.Slice(results, func(i, j int) bool {
return results[i].Name < results[j].Name
})
expectations := []string{"default/Pod1/test-container1", "default/Pod1/test-container2", "default/Pod4/test-container3"}
for i, expectation := range expectations {
require.Equal(t, expectation, results[i].Name)
for _, failure := range results[i].Error {
require.Equal(t, "fake logs", failure.Text)
}
}
}

View File

@@ -151,8 +151,10 @@ func (MutatingWebhookAnalyzer) Analyze(a common.Analyzer) ([]common.Result, erro
Error: value.FailureDetails,
}
parent, _ := util.GetParent(a.Client, value.MutatingWebhook.ObjectMeta)
currentAnalysis.ParentObject = parent
parent, found := util.GetParent(a.Client, value.MutatingWebhook.ObjectMeta)
if found {
currentAnalysis.ParentObject = parent
}
a.Results = append(a.Results, currentAnalysis)
}

View File

@@ -136,6 +136,19 @@ func TestNetpolWithPod(t *testing.T) {
func TestNetpolNoPodsNamespaceFiltering(t *testing.T) {
clientset := fake.NewSimpleClientset(
&networkingv1.NetworkPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "policy-without-podselector-match-labels",
Namespace: "default",
},
Spec: networkingv1.NetworkPolicySpec{
PodSelector: metav1.LabelSelector{
// len(MatchLabels) == 0 should trigger a failure.
// Allowing traffic to all pods.
MatchLabels: map[string]string{},
},
},
},
&networkingv1.NetworkPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "example",
@@ -203,7 +216,7 @@ func TestNetpolNoPodsNamespaceFiltering(t *testing.T) {
t.Error(err)
}
assert.Equal(t, len(results), 1)
assert.Equal(t, len(results), 2)
assert.Equal(t, results[0].Kind, "NetworkPolicy")
}

View File

@@ -74,8 +74,10 @@ func (NodeAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
Error: value.FailureDetails,
}
parent, _ := util.GetParent(a.Client, value.Node.ObjectMeta)
currentAnalysis.ParentObject = parent
parent, found := util.GetParent(a.Client, value.Node.ObjectMeta)
if found {
currentAnalysis.ParentObject = parent
}
a.Results = append(a.Results, currentAnalysis)
}

View File

@@ -15,110 +15,155 @@ 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"
)
func TestNodeAnalyzerNodeReady(t *testing.T) {
clientset := fake.NewSimpleClientset(&v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node1",
},
Status: v1.NodeStatus{
Conditions: []v1.NodeCondition{
{
Type: v1.NodeReady,
Status: v1.ConditionTrue,
Reason: "KubeletReady",
Message: "kubelet is posting ready status",
},
},
},
})
func TestNodeAnalyzer(t *testing.T) {
config := common.Analyzer{
Client: &kubernetes.Client{
Client: clientset,
},
Context: context.Background(),
}
nodeAnalyzer := NodeAnalyzer{}
var analysisResults []common.Result
analysisResults, err := nodeAnalyzer.Analyze(config)
if err != nil {
t.Error(err)
}
assert.Equal(t, len(analysisResults), 0)
}
func TestNodeAnalyzerNodeDiskPressure(t *testing.T) {
clientset := fake.NewSimpleClientset(&v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node1",
},
Status: v1.NodeStatus{
Conditions: []v1.NodeCondition{
{
Type: v1.NodeDiskPressure,
Status: v1.ConditionTrue,
Reason: "KubeletHasDiskPressure",
Message: "kubelet has disk pressure",
Client: fake.NewSimpleClientset(
&v1.Node{
// A node without Status Conditions shouldn't contribute to failures.
ObjectMeta: metav1.ObjectMeta{
Name: "Node1",
Namespace: "test",
},
},
},
&v1.Node{
// Nodes are not filtered using namespace.
ObjectMeta: metav1.ObjectMeta{
Name: "Node2",
Namespace: "default",
},
Status: v1.NodeStatus{
Conditions: []v1.NodeCondition{
{
// Won't contribute to failures.
Type: v1.NodeReady,
Status: v1.ConditionTrue,
},
{
// Will contribute to failures.
Type: v1.NodeReady,
Status: v1.ConditionFalse,
},
{
// Will contribute to failures.
Type: v1.NodeReady,
Status: v1.ConditionUnknown,
},
// Non-false statuses for the default cases contribute to failures.
{
Type: v1.NodeMemoryPressure,
Status: v1.ConditionTrue,
},
{
Type: v1.NodeDiskPressure,
Status: v1.ConditionTrue,
},
{
Type: v1.NodePIDPressure,
Status: v1.ConditionTrue,
},
{
Type: v1.NodeNetworkUnavailable,
Status: v1.ConditionTrue,
},
{
Type: v1.NodeMemoryPressure,
Status: v1.ConditionUnknown,
},
{
Type: v1.NodeDiskPressure,
Status: v1.ConditionUnknown,
},
{
Type: v1.NodePIDPressure,
Status: v1.ConditionUnknown,
},
{
Type: v1.NodeNetworkUnavailable,
Status: v1.ConditionUnknown,
},
// A cloud provider may set their own condition and/or a new status
// might be introduced. In such cases a failure is assumed and
// the code shouldn't break, although it might be a false positive.
{
Type: "UnknownNodeConditionType",
Status: "CompletelyUnknown",
},
// These won't contribute to failures.
{
Type: v1.NodeMemoryPressure,
Status: v1.ConditionFalse,
},
{
Type: v1.NodeDiskPressure,
Status: v1.ConditionFalse,
},
{
Type: v1.NodePIDPressure,
Status: v1.ConditionFalse,
},
{
Type: v1.NodeNetworkUnavailable,
Status: v1.ConditionFalse,
},
},
},
},
&v1.Node{
// A node without any failures shouldn't be present in the results.
ObjectMeta: metav1.ObjectMeta{
Name: "Node3",
Namespace: "test",
},
Status: v1.NodeStatus{
Conditions: []v1.NodeCondition{
{
// Won't contribute to failures.
Type: v1.NodeReady,
Status: v1.ConditionTrue,
},
},
},
},
),
},
Context: context.Background(),
Namespace: "test",
}
nAnalyzer := NodeAnalyzer{}
results, err := nAnalyzer.Analyze(config)
require.NoError(t, err)
sort.Slice(results, func(i, j int) bool {
return results[i].Name < results[j].Name
})
config := common.Analyzer{
Client: &kubernetes.Client{
Client: clientset,
expectations := []struct {
name string
failuresCount int
}{
{
name: "Node2",
failuresCount: 11,
},
Context: context.Background(),
}
nodeAnalyzer := NodeAnalyzer{}
var analysisResults []common.Result
analysisResults, err := nodeAnalyzer.Analyze(config)
if err != nil {
t.Error(err)
}
assert.Equal(t, len(analysisResults), 1)
}
// A cloud provider may set their own condition and/or a new status might be introduced
// In such cases a failure is assumed and the code shouldn't break, although it might be a false positive
func TestNodeAnalyzerNodeUnknownType(t *testing.T) {
clientset := fake.NewSimpleClientset(&v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node1",
},
Status: v1.NodeStatus{
Conditions: []v1.NodeCondition{
{
Type: "UnknownNodeConditionType",
Status: "CompletelyUnknown",
Reason: "KubeletHasTheUnknown",
Message: "kubelet has the unknown",
},
},
},
})
require.Equal(t, len(expectations), len(results))
config := common.Analyzer{
Client: &kubernetes.Client{
Client: clientset,
},
Context: context.Background(),
for i, result := range results {
require.Equal(t, expectations[i].name, result.Name)
require.Equal(t, expectations[i].failuresCount, len(result.Error))
}
nodeAnalyzer := NodeAnalyzer{}
var analysisResults []common.Result
analysisResults, err := nodeAnalyzer.Analyze(config)
if err != nil {
t.Error(err)
}
assert.Equal(t, len(analysisResults), 1)
}

View File

@@ -99,8 +99,10 @@ func (PdbAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
Error: value.FailureDetails,
}
parent, _ := util.GetParent(a.Client, value.PodDisruptionBudget.ObjectMeta)
currentAnalysis.ParentObject = parent
parent, found := util.GetParent(a.Client, value.PodDisruptionBudget.ObjectMeta)
if found {
currentAnalysis.ParentObject = parent
}
a.Results = append(a.Results, currentAnalysis)
}

View File

@@ -112,11 +112,6 @@ func TestPodDisruptionBudgetAnalyzer(t *testing.T) {
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")
}
}
require.Equal(t, 1, len(results))
require.Equal(t, "test/PDB3", results[0].Name)
}

View File

@@ -18,6 +18,7 @@ import (
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@@ -41,12 +42,12 @@ func (PodAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
for _, pod := range list.Items {
var failures []common.Failure
// Check for pending pods
if pod.Status.Phase == "Pending" {
// Check through container status to check for crashes
for _, containerStatus := range pod.Status.Conditions {
if containerStatus.Type == "PodScheduled" && containerStatus.Reason == "Unschedulable" {
if containerStatus.Type == v1.PodScheduled && containerStatus.Reason == "Unschedulable" {
if containerStatus.Message != "" {
failures = append(failures, common.Failure{
Text: containerStatus.Message,
@@ -57,60 +58,12 @@ func (PodAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
}
}
// Check through container status to check for crashes or unready
for _, containerStatus := range pod.Status.ContainerStatuses {
// Check for errors in the init containers.
failures = append(failures, analyzeContainerStatusFailures(a, pod.Status.InitContainerStatuses, pod.Name, pod.Namespace, string(pod.Status.Phase))...)
if containerStatus.State.Waiting != nil {
// Check for errors in containers.
failures = append(failures, analyzeContainerStatusFailures(a, pod.Status.ContainerStatuses, pod.Name, pod.Namespace, string(pod.Status.Phase))...)
if isErrorReason(containerStatus.State.Waiting.Reason) && containerStatus.State.Waiting.Message != "" {
failures = append(failures, common.Failure{
Text: containerStatus.State.Waiting.Message,
Sensitive: []common.Sensitive{},
})
}
// This represents a container that is still being created or blocked due to conditions such as OOMKilled
if containerStatus.State.Waiting.Reason == "ContainerCreating" && pod.Status.Phase == "Pending" {
// parse the event log and append details
evt, err := FetchLatestEvent(a.Context, a.Client, pod.Namespace, pod.Name)
if err != nil || evt == nil {
continue
}
if isEvtErrorReason(evt.Reason) && evt.Message != "" {
failures = append(failures, common.Failure{
Text: evt.Message,
Sensitive: []common.Sensitive{},
})
}
}
// This represents container that is in CrashLoopBackOff state due to conditions such as OOMKilled
if containerStatus.State.Waiting.Reason == "CrashLoopBackOff" {
failures = append(failures, common.Failure{
Text: fmt.Sprintf("the last termination reason is %s container=%s pod=%s", containerStatus.LastTerminationState.Terminated.Reason, containerStatus.Name, pod.Name),
Sensitive: []common.Sensitive{},
})
}
} else {
// when pod is Running but its ReadinessProbe fails
if !containerStatus.Ready && pod.Status.Phase == "Running" {
// parse the event log and append details
evt, err := FetchLatestEvent(a.Context, a.Client, pod.Namespace, pod.Name)
if err != nil || evt == nil {
continue
}
if evt.Reason == "Unhealthy" && evt.Message != "" {
failures = append(failures, common.Failure{
Text: evt.Message,
Sensitive: []common.Sensitive{},
})
}
}
}
}
if len(failures) > 0 {
preAnalysis[fmt.Sprintf("%s/%s", pod.Namespace, pod.Name)] = common.PreAnalysis{
Pod: pod,
@@ -127,14 +80,68 @@ func (PodAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
Error: value.FailureDetails,
}
parent, _ := util.GetParent(a.Client, value.Pod.ObjectMeta)
currentAnalysis.ParentObject = parent
parent, found := util.GetParent(a.Client, value.Pod.ObjectMeta)
if found {
currentAnalysis.ParentObject = parent
}
a.Results = append(a.Results, currentAnalysis)
}
return a.Results, nil
}
func analyzeContainerStatusFailures(a common.Analyzer, statuses []v1.ContainerStatus, name string, namespace string, statusPhase string) []common.Failure {
var failures []common.Failure
// Check through container status to check for crashes or unready
for _, containerStatus := range statuses {
if containerStatus.State.Waiting != nil {
if containerStatus.State.Waiting.Reason == "ContainerCreating" && statusPhase == "Pending" {
// This represents a container that is still being created or blocked due to conditions such as OOMKilled
// parse the event log and append details
evt, err := util.FetchLatestEvent(a.Context, a.Client, namespace, name)
if err != nil || evt == nil {
continue
}
if isEvtErrorReason(evt.Reason) && evt.Message != "" {
failures = append(failures, common.Failure{
Text: evt.Message,
Sensitive: []common.Sensitive{},
})
}
} else if containerStatus.State.Waiting.Reason == "CrashLoopBackOff" && containerStatus.LastTerminationState.Terminated != nil {
// This represents container that is in CrashLoopBackOff state due to conditions such as OOMKilled
failures = append(failures, common.Failure{
Text: fmt.Sprintf("the last termination reason is %s container=%s pod=%s", containerStatus.LastTerminationState.Terminated.Reason, containerStatus.Name, name),
Sensitive: []common.Sensitive{},
})
} else if isErrorReason(containerStatus.State.Waiting.Reason) && containerStatus.State.Waiting.Message != "" {
failures = append(failures, common.Failure{
Text: containerStatus.State.Waiting.Message,
Sensitive: []common.Sensitive{},
})
}
} else {
// when pod is Running but its ReadinessProbe fails
if !containerStatus.Ready && statusPhase == "Running" {
// parse the event log and append details
evt, err := util.FetchLatestEvent(a.Context, a.Client, namespace, name)
if err != nil || evt == nil {
continue
}
if evt.Reason == "Unhealthy" && evt.Message != "" {
failures = append(failures, common.Failure{
Text: evt.Message,
Sensitive: []common.Sensitive{},
})
}
}
}
}
return failures
}
func isErrorReason(reason string) bool {
failureReasons := []string{
"CrashLoopBackOff", "ImagePullBackOff", "CreateContainerConfigError", "PreCreateHookError", "CreateContainerError",

View File

@@ -15,144 +15,357 @@ 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"
)
func TestPodAnalyzer(t *testing.T) {
clientset := fake.NewSimpleClientset(
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "example",
Namespace: "default",
Annotations: map[string]string{},
},
Status: v1.PodStatus{
Phase: v1.PodPending,
Conditions: []v1.PodCondition{
{
Type: v1.PodScheduled,
Reason: "Unschedulable",
Message: "0/1 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate.",
},
tests := []struct {
name string
config common.Analyzer
expectations []struct {
name string
failuresCount int
}
}{
{
name: "Pending pods, namespace filtering and readiness probe failure",
config: common.Analyzer{
Client: &kubernetes.Client{
Client: fake.NewSimpleClientset(
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "Pod1",
Namespace: "default",
},
Status: v1.PodStatus{
Phase: v1.PodPending,
Conditions: []v1.PodCondition{
{
// This condition will contribute to failures.
Type: v1.PodScheduled,
Reason: "Unschedulable",
Message: "0/1 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate.",
},
{
// This condition won't contribute to failures.
Type: v1.PodScheduled,
Reason: "Unexpected failure",
},
},
},
},
&v1.Pod{
// This pod won't be selected because of namespace filtering.
ObjectMeta: metav1.ObjectMeta{
Name: "Pod2",
Namespace: "test",
},
},
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "Pod3",
Namespace: "default",
},
Status: v1.PodStatus{
// When pod is Running but its ReadinessProbe fails
Phase: v1.PodRunning,
ContainerStatuses: []v1.ContainerStatus{
{
Ready: false,
},
},
},
},
&v1.Event{
ObjectMeta: metav1.ObjectMeta{
Name: "Event1",
Namespace: "default",
},
InvolvedObject: v1.ObjectReference{
Kind: "Pod",
Name: "Pod3",
Namespace: "default",
},
Reason: "Unhealthy",
Message: "readiness probe failed: the detail reason here ...",
Source: v1.EventSource{Component: "eventTest"},
Count: 1,
Type: v1.EventTypeWarning,
},
),
},
},
},
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "example2",
Context: context.Background(),
Namespace: "default",
},
Status: v1.PodStatus{
Phase: v1.PodRunning,
ContainerStatuses: []v1.ContainerStatus{
{
Name: "example2",
Ready: false,
},
expectations: []struct {
name string
failuresCount int
}{
{
name: "default/Pod1",
failuresCount: 1,
},
Conditions: []v1.PodCondition{
{
Type: v1.ContainersReady,
Reason: "ContainersNotReady",
Message: "containers with unready status: [example2]",
},
{
name: "default/Pod3",
failuresCount: 1,
},
},
},
// simulate event: 30s Warning Unhealthy pod/my-nginx-7fb4dbcf47-4ch4w Readiness probe failed: bash: xxxx: command not found
&v1.Event{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
{
name: "readiness probe failure without any event",
config: common.Analyzer{
Client: &kubernetes.Client{
Client: fake.NewSimpleClientset(
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "Pod1",
Namespace: "default",
},
Status: v1.PodStatus{
// When pod is Running but its ReadinessProbe fails
// It won't contribute to any failures because
// there's no event present.
Phase: v1.PodRunning,
ContainerStatuses: []v1.ContainerStatus{
{
Ready: false,
},
},
},
},
),
},
Context: context.Background(),
Namespace: "default",
},
InvolvedObject: v1.ObjectReference{
Kind: "Pod",
Name: "example2",
Namespace: "default",
UID: "differentUid",
APIVersion: "v1",
},
Reason: "Unhealthy",
Message: "readiness probe failed: the detail reason here ...",
Source: v1.EventSource{Component: "eventTest"},
Count: 1,
Type: v1.EventTypeWarning,
})
config := common.Analyzer{
Client: &kubernetes.Client{
Client: clientset,
},
Context: context.Background(),
Namespace: "default",
}
podAnalyzer := PodAnalyzer{}
var analysisResults []common.Result
analysisResults, err := podAnalyzer.Analyze(config)
if err != nil {
t.Error(err)
}
assert.Equal(t, len(analysisResults), 2)
}
func TestPodAnalyzerNamespaceFiltering(t *testing.T) {
clientset := fake.NewSimpleClientset(
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "example",
Namespace: "default",
Annotations: map[string]string{},
{
name: "Init container status state waiting",
config: common.Analyzer{
Client: &kubernetes.Client{
Client: fake.NewSimpleClientset(
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "Pod1",
Namespace: "default",
},
Status: v1.PodStatus{
Phase: v1.PodPending,
InitContainerStatuses: []v1.ContainerStatus{
{
Ready: true,
State: v1.ContainerState{
Running: &v1.ContainerStateRunning{
StartedAt: metav1.Now(),
},
},
},
{
Ready: false,
State: v1.ContainerState{
Waiting: &v1.ContainerStateWaiting{
// This represents a container that is still being created or blocked due to conditions such as OOMKilled
Reason: "ContainerCreating",
},
},
},
},
},
},
&v1.Event{
ObjectMeta: metav1.ObjectMeta{
Name: "Event1",
Namespace: "default",
},
InvolvedObject: v1.ObjectReference{
Kind: "Pod",
Name: "Pod1",
Namespace: "default",
},
Reason: "FailedCreatePodSandBox",
Message: "failed to create the pod sandbox ...",
Type: v1.EventTypeWarning,
},
),
},
Context: context.Background(),
Namespace: "default",
},
Status: v1.PodStatus{
Phase: v1.PodPending,
Conditions: []v1.PodCondition{
{
Type: v1.PodScheduled,
Reason: "Unschedulable",
Message: "0/1 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate.",
},
expectations: []struct {
name string
failuresCount int
}{
{
name: "default/Pod1",
failuresCount: 1,
},
},
},
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "example",
Namespace: "other-namespace",
Annotations: map[string]string{},
{
name: "Container status state waiting but no event reported",
config: common.Analyzer{
Client: &kubernetes.Client{
Client: fake.NewSimpleClientset(
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "Pod1",
Namespace: "default",
},
Status: v1.PodStatus{
Phase: v1.PodPending,
ContainerStatuses: []v1.ContainerStatus{
{
Ready: false,
State: v1.ContainerState{
Waiting: &v1.ContainerStateWaiting{
// This represents a container that is still being created or blocked due to conditions such as OOMKilled
Reason: "ContainerCreating",
},
},
},
},
},
},
),
},
Context: context.Background(),
Namespace: "default",
},
Status: v1.PodStatus{
Phase: v1.PodPending,
Conditions: []v1.PodCondition{
{
Type: v1.PodScheduled,
Reason: "Unschedulable",
Message: "0/1 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate.",
},
},
{
name: "Container status state waiting",
config: common.Analyzer{
Client: &kubernetes.Client{
Client: fake.NewSimpleClientset(
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "Pod1",
Namespace: "default",
},
Status: v1.PodStatus{
Phase: v1.PodPending,
ContainerStatuses: []v1.ContainerStatus{
{
Name: "Container1",
Ready: false,
State: v1.ContainerState{
Waiting: &v1.ContainerStateWaiting{
// This represents a container that is still being created or blocked due to conditions such as OOMKilled
Reason: "ContainerCreating",
},
},
},
{
Name: "Container2",
Ready: false,
State: v1.ContainerState{
Waiting: &v1.ContainerStateWaiting{
// This represents container that is in CrashLoopBackOff state due to conditions such as OOMKilled
Reason: "CrashLoopBackOff",
},
},
LastTerminationState: v1.ContainerState{
Terminated: &v1.ContainerStateTerminated{
Reason: "test reason",
},
},
},
{
Name: "Container3",
Ready: false,
State: v1.ContainerState{
Waiting: &v1.ContainerStateWaiting{
// This won't contribute to failures.
Reason: "RandomReason",
Message: "This container won't be present in the failures",
},
},
},
{
Name: "Container4",
Ready: false,
State: v1.ContainerState{
Waiting: &v1.ContainerStateWaiting{
// Valid error reason.
Reason: "PreStartHookError",
Message: "Container4 encountered PreStartHookError",
},
},
},
{
Name: "Container5",
Ready: false,
State: v1.ContainerState{
Waiting: &v1.ContainerStateWaiting{
// Valid error reason.
Reason: "CrashLoopBackOff",
Message: "Container4 encountered CrashLoopBackOff",
},
},
},
},
},
},
&v1.Event{
ObjectMeta: metav1.ObjectMeta{
Name: "Event1",
Namespace: "default",
},
InvolvedObject: v1.ObjectReference{
Kind: "Pod",
Name: "Pod1",
Namespace: "default",
},
// This reason won't contribute to failures.
Reason: "RandomEvent",
Type: v1.EventTypeWarning,
},
),
},
Context: context.Background(),
Namespace: "default",
},
expectations: []struct {
name string
failuresCount int
}{
{
name: "default/Pod1",
failuresCount: 3,
},
},
})
config := common.Analyzer{
Client: &kubernetes.Client{
Client: clientset,
},
Context: context.Background(),
Namespace: "default",
}
podAnalyzer := PodAnalyzer{}
var analysisResults []common.Result
analysisResults, err := podAnalyzer.Analyze(config)
if err != nil {
t.Error(err)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
results, err := podAnalyzer.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, result := range results {
require.Equal(t, tt.expectations[i].name, result.Name)
require.Equal(t, tt.expectations[i].failuresCount, len(result.Error))
}
}
})
}
assert.Equal(t, len(analysisResults), 1)
}

View File

@@ -47,7 +47,7 @@ func (PvcAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
if pvc.Status.Phase == appsv1.ClaimPending {
// parse the event log and append details
evt, err := FetchLatestEvent(a.Context, a.Client, pvc.Namespace, pvc.Name)
evt, err := util.FetchLatestEvent(a.Context, a.Client, pvc.Namespace, pvc.Name)
if err != nil || evt == nil {
continue
}
@@ -74,8 +74,10 @@ func (PvcAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
Error: value.FailureDetails,
}
parent, _ := util.GetParent(a.Client, value.PersistentVolumeClaim.ObjectMeta)
currentAnalysis.ParentObject = parent
parent, found := util.GetParent(a.Client, value.PersistentVolumeClaim.ObjectMeta)
if found {
currentAnalysis.ParentObject = parent
}
a.Results = append(a.Results, currentAnalysis)
}

View File

@@ -17,6 +17,7 @@ import (
"context"
"sort"
"testing"
"time"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
@@ -38,13 +39,15 @@ func TestPersistentVolumeClaimAnalyzer(t *testing.T) {
Client: &kubernetes.Client{
Client: fake.NewSimpleClientset(
&appsv1.Event{
// This is the latest event.
ObjectMeta: metav1.ObjectMeta{
Name: "Event1",
Namespace: "default",
},
LastTimestamp: metav1.Time{
Time: time.Date(2024, 3, 15, 10, 0, 0, 0, time.UTC),
},
Reason: "ProvisioningFailed",
Message: "PVC provisioning failed",
Message: "PVC Event1 provisioning failed",
},
&appsv1.Event{
ObjectMeta: metav1.ObjectMeta{
@@ -54,10 +57,16 @@ func TestPersistentVolumeClaimAnalyzer(t *testing.T) {
},
},
&appsv1.Event{
// This is the latest event.
ObjectMeta: metav1.ObjectMeta{
Name: "Event3",
Namespace: "default",
},
LastTimestamp: metav1.Time{
Time: time.Date(2024, 4, 15, 10, 0, 0, 0, time.UTC),
},
Reason: "ProvisioningFailed",
Message: "PVC Event3 provisioning failed",
},
&appsv1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
@@ -214,9 +223,6 @@ func TestPersistentVolumeClaimAnalyzer(t *testing.T) {
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)
}
}
}
})

View File

@@ -72,8 +72,10 @@ func (ReplicaSetAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
Error: value.FailureDetails,
}
parent, _ := util.GetParent(a.Client, value.ReplicaSet.ObjectMeta)
currentAnalysis.ParentObject = parent
parent, found := util.GetParent(a.Client, value.ReplicaSet.ObjectMeta)
if found {
currentAnalysis.ParentObject = parent
}
a.Results = append(a.Results, currentAnalysis)
}
return a.Results, nil

View File

@@ -124,30 +124,23 @@ func TestReplicaSetAnalyzer(t *testing.T) {
})
expectations := []struct {
name string
failuresText []string
name string
failuresCount int
}{
{
name: "default/ReplicaSet1",
failuresText: []string{
"failed to create test replica set 1",
},
name: "default/ReplicaSet1",
failuresCount: 1,
},
{
name: "default/ReplicaSet4",
failuresText: []string{
"failed to create test replica set 4 condition 1",
"failed to create test replica set 4 condition 3",
},
name: "default/ReplicaSet4",
failuresCount: 2,
},
}
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)
}
for i, result := range results {
require.Equal(t, expectations[i].name, result.Name)
require.Equal(t, expectations[i].failuresCount, len(result.Error))
}
}

View File

@@ -128,8 +128,10 @@ func (ServiceAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
Error: value.FailureDetails,
}
parent, _ := util.GetParent(a.Client, value.Endpoint.ObjectMeta)
currentAnalysis.ParentObject = parent
parent, found := util.GetParent(a.Client, value.Endpoint.ObjectMeta)
if found {
currentAnalysis.ParentObject = parent
}
a.Results = append(a.Results, currentAnalysis)
}
return a.Results, nil

View File

@@ -145,21 +145,16 @@ func TestServiceAnalyzer(t *testing.T) {
})
expectations := []struct {
name string
failuresText []string
name string
failuresCount int
}{
{
name: "test/Endpoint1",
failuresText: []string{
"Service has not ready endpoints, pods",
},
name: "test/Endpoint1",
failuresCount: 1,
},
{
name: "test/Service1",
failuresText: []string{
"Service has no endpoints, expected label",
"Service has no endpoints, expected label",
},
name: "test/Service1",
failuresCount: 2,
},
}
@@ -167,8 +162,6 @@ func TestServiceAnalyzer(t *testing.T) {
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])
}
require.Equal(t, expectations[i].failuresCount, len(result.Error))
}
}

View File

@@ -109,8 +109,10 @@ func (StatefulSetAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
Error: value.FailureDetails,
}
parent, _ := util.GetParent(a.Client, value.StatefulSet.ObjectMeta)
currentAnalysis.ParentObject = parent
parent, found := util.GetParent(a.Client, value.StatefulSet.ObjectMeta)
if found {
currentAnalysis.ParentObject = parent
}
a.Results = append(a.Results, currentAnalysis)
}

View File

@@ -149,8 +149,10 @@ func (ValidatingWebhookAnalyzer) Analyze(a common.Analyzer) ([]common.Result, er
Error: value.FailureDetails,
}
parent, _ := util.GetParent(a.Client, value.ValidatingWebhook.ObjectMeta)
currentAnalysis.ParentObject = parent
parent, found := util.GetParent(a.Client, value.ValidatingWebhook.ObjectMeta)
if found {
currentAnalysis.ParentObject = parent
}
a.Results = append(a.Results, currentAnalysis)
}

4
pkg/cache/cache.go vendored
View File

@@ -47,7 +47,7 @@ func ParseCacheConfiguration() (CacheProvider, error) {
return cacheInfo, nil
}
func NewCacheProvider(cacheType, bucketname, region, storageAccount, containerName, projectId string) (CacheProvider, error) {
func NewCacheProvider(cacheType, bucketname, region, endpoint, storageAccount, containerName, projectId string, insecure bool) (CacheProvider, error) {
cProvider := CacheProvider{}
switch {
@@ -61,6 +61,8 @@ func NewCacheProvider(cacheType, bucketname, region, storageAccount, containerNa
case cacheType == "s3":
cProvider.S3.BucketName = bucketname
cProvider.S3.Region = region
cProvider.S3.Endpoint = endpoint
cProvider.S3.InsecureSkipVerify = insecure
default:
return CacheProvider{}, status.Error(codes.Internal, fmt.Sprintf("%s is not a valid option", cacheType))
}

20
pkg/cache/s3_based.go vendored
View File

@@ -2,7 +2,9 @@ package cache
import (
"bytes"
"crypto/tls"
"log"
"net/http"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
@@ -17,17 +19,16 @@ type S3Cache struct {
}
type S3CacheConfiguration struct {
Region string `mapstructure:"region" yaml:"region,omitempty"`
BucketName string `mapstructure:"bucketname" yaml:"bucketname,omitempty"`
Region string `mapstructure:"region" yaml:"region,omitempty"`
BucketName string `mapstructure:"bucketname" yaml:"bucketname,omitempty"`
Endpoint string `mapstructure:"endpoint" yaml:"endpoint,omitempty"`
InsecureSkipVerify bool `mapstructure:"insecure" yaml:"insecure,omitempty"`
}
func (s *S3Cache) Configure(cacheInfo CacheProvider) error {
if cacheInfo.S3.BucketName == "" {
log.Fatal("Bucket name not configured")
}
if cacheInfo.S3.Region == "" {
log.Fatal("Region not configured")
}
s.bucketName = cacheInfo.S3.BucketName
sess := session.Must(session.NewSessionWithOptions(session.Options{
@@ -36,6 +37,15 @@ func (s *S3Cache) Configure(cacheInfo CacheProvider) error {
Region: aws.String(cacheInfo.S3.Region),
},
}))
if cacheInfo.S3.Endpoint != "" {
sess.Config.Endpoint = &cacheInfo.S3.Endpoint
sess.Config.S3ForcePathStyle = aws.Bool(true)
transport := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: cacheInfo.S3.InsecureSkipVerify},
}
customClient := &http.Client{Transport: transport}
sess.Config.HTTPClient = customClient
}
s3Client := s3.New(sess)

View File

@@ -20,6 +20,7 @@ import (
openapi_v2 "github.com/google/gnostic/openapiv2"
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
keda "github.com/kedacore/keda/v2/apis/keda/v1alpha1"
regv1 "k8s.io/api/admissionregistration/v1"
appsv1 "k8s.io/api/apps/v1"
autov1 "k8s.io/api/autoscaling/v1"
@@ -62,6 +63,7 @@ type PreAnalysis struct {
Gateway gtwapi.Gateway
HTTPRoute gtwapi.HTTPRoute
// Integrations
ScaledObject keda.ScaledObject
TrivyVulnerabilityReport trivy.VulnerabilityReport
TrivyConfigAuditReport trivy.ConfigAuditReport
}

View File

@@ -1,11 +1,12 @@
package aws
import (
"os"
"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 {
@@ -23,16 +24,28 @@ func (a *AWS) UnDeploy(namespace string) error {
}
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")
// Retrieve AWS credentials from the environment
accessKeyID := os.Getenv("AWS_ACCESS_KEY_ID")
secretAccessKey := os.Getenv("AWS_SECRET_ACCESS_KEY")
awsProfile := os.Getenv("AWS_PROFILE")
var sess *session.Session
if accessKeyID != "" && secretAccessKey != "" {
// Use access keys if both are provided
sess = session.Must(session.NewSessionWithOptions(session.Options{
Config: aws.Config{},
}))
} else {
// Use AWS profile, default to "default" if not set
if awsProfile == "" {
awsProfile = "default"
}
sess = session.Must(session.NewSessionWithOptions(session.Options{
Profile: awsProfile,
SharedConfigState: session.SharedConfigEnable,
}))
}
sess := session.Must(session.NewSessionWithOptions(session.Options{
SharedConfigState: session.SharedConfigEnable,
Config: aws.Config{},
}))
a.sess = sess
(*mergedMap)["EKS"] = &EKSAnalyzer{
session: a.sess,

View File

@@ -16,9 +16,11 @@ 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/keda"
"github.com/k8sgpt-ai/k8sgpt/pkg/integration/prometheus"
"github.com/k8sgpt-ai/k8sgpt/pkg/integration/trivy"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
@@ -49,6 +51,7 @@ var integrations = map[string]IIntegration{
"trivy": trivy.NewTrivy(),
"prometheus": prometheus.NewPrometheus(),
"aws": aws.NewAWS(),
"keda": keda.NewKeda(),
}
func NewIntegration() *Integration {
@@ -89,7 +92,7 @@ func (*Integration) Activate(name string, namespace string, activeFilters []stri
if !skipInstall {
if err := integrations[name].Deploy(namespace); err != nil {
return err
return fmt.Errorf("failed to deploy %s integration: %w", name, err)
}
}
mergedFilters := activeFilters
@@ -124,7 +127,7 @@ func (*Integration) Deactivate(name string, namespace string) error {
}
if err := integrations[name].UnDeploy(namespace); err != nil {
return err
return fmt.Errorf("failed to undeploy %s integration: %w", name, err)
}
viper.Set("active_filters", activeFilters)

View File

@@ -0,0 +1,142 @@
/*
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 integration
import (
"os"
"testing"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
)
func TestAnalyzerByIntegration(t *testing.T) {
integration := NewIntegration()
_, err := integration.Get("invalid-name")
require.ErrorContains(t, err, "integration not found")
tests := []struct {
name string
expectedName string
expectedErr string
}{
{
name: "random",
expectedErr: "analyzerbyintegration: no matches found",
},
{
name: "PrometheusConfigValidate",
expectedName: "prometheus",
},
{
name: "PrometheusConfigRelabelReport",
expectedName: "prometheus",
},
{
name: "VulnerabilityReport",
expectedName: "trivy",
},
{
name: "ConfigAuditReport",
expectedName: "trivy",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
name, err := integration.AnalyzerByIntegration(tt.name)
if tt.expectedErr == "" {
require.NoError(t, err)
require.Equal(t, tt.expectedName, name)
} else {
require.ErrorContains(t, err, tt.expectedErr)
require.Empty(t, name)
}
})
}
}
func TestActivate(t *testing.T) {
integration := NewIntegration()
err := integration.Activate("prometheus", "", []string{}, true)
require.ErrorContains(t, err, "error writing config file:")
err = integration.Deactivate("prometheus", "")
require.ErrorContains(t, err, "error writing config file:")
configFileName := "config.json"
_, err = os.CreateTemp("", configFileName)
require.NoError(t, err)
defer os.Remove(configFileName)
// Set the configuration file in viper
viper.SetConfigType("json")
viper.SetConfigFile(configFileName)
inteNotFoundErr := "integration not found"
tests := []struct {
name string
namespace string
activeFilters []string
skipInstall bool
expectedIsActivate bool
expectedActivationErr string
expectedIsActivateError string
expectedDeactivationErr string
}{
{
name: "invalid integration",
expectedActivationErr: inteNotFoundErr,
expectedIsActivateError: inteNotFoundErr,
expectedDeactivationErr: inteNotFoundErr,
},
{
name: "prometheus",
skipInstall: true,
expectedIsActivate: true,
},
{
name: "trivy",
skipInstall: false,
expectedActivationErr: "failed to deploy trivy integration:",
expectedDeactivationErr: "failed to undeploy trivy integration:",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
err := integration.Activate(tt.name, tt.namespace, tt.activeFilters, tt.skipInstall)
if tt.expectedActivationErr == "" {
require.NoError(t, err)
} else {
require.ErrorContains(t, err, tt.expectedActivationErr)
}
ok, err := integration.IsActivate(tt.name)
if tt.expectedIsActivateError == "" {
require.NoError(t, err)
require.Equal(t, tt.expectedIsActivate, ok)
} else {
require.ErrorContains(t, err, tt.expectedIsActivateError)
}
err = integration.Deactivate(tt.name, tt.namespace)
if tt.expectedDeactivationErr == "" {
require.NoError(t, err)
} else {
require.ErrorContains(t, err, tt.expectedDeactivationErr)
}
})
}
}

View File

@@ -0,0 +1,229 @@
package keda
import (
"context"
"fmt"
"os"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/kedacore/keda/v2/pkg/generated/clientset/versioned/typed/keda/v1alpha1"
helmclient "github.com/mittwald/go-helm-client"
"github.com/spf13/viper"
"helm.sh/helm/v3/pkg/repo"
)
var (
Repo = getEnv("KEDA_REPO", "https://kedacore.github.io/charts")
Version = getEnv("KEDA_VERSION", "2.11.2")
ChartName = getEnv("KEDA_CHART_NAME", "keda")
RepoShortName = getEnv("KEDA_REPO_SHORT_NAME", "keda")
ReleaseName = getEnv("KEDA_RELEASE_NAME", "keda-k8sgpt")
)
type Keda struct {
helm helmclient.Client
}
func getEnv(key, defaultValue string) string {
value := os.Getenv(key)
if value == "" {
return defaultValue
}
return value
}
func NewKeda() *Keda {
helmClient, err := helmclient.New(&helmclient.Options{})
if err != nil {
panic(err)
}
return &Keda{
helm: helmClient,
}
}
func (k *Keda) Deploy(namespace string) error {
// Add the repository
chartRepo := repo.Entry{
Name: RepoShortName,
URL: Repo,
}
// Add a chart-repository to the client.
if err := k.helm.AddOrUpdateChartRepo(chartRepo); err != nil {
panic(err)
}
chartSpec := helmclient.ChartSpec{
ReleaseName: ReleaseName,
ChartName: fmt.Sprintf("%s/%s", RepoShortName, ChartName),
Namespace: namespace,
//TODO: All of this should be configurable
UpgradeCRDs: true,
Wait: false,
Timeout: 300,
CreateNamespace: true,
}
// Install a chart release.
// Note that helmclient.Options.Namespace should ideally match the namespace in chartSpec.Namespace.
if _, err := k.helm.InstallOrUpgradeChart(context.Background(), &chartSpec, nil); err != nil {
return err
}
return nil
}
func (k *Keda) UnDeploy(namespace string) error {
kubecontext := viper.GetString("kubecontext")
kubeconfig := viper.GetString("kubeconfig")
client, err := kubernetes.NewClient(kubecontext, kubeconfig)
if err != nil {
// TODO: better error handling
color.Red("Error initialising kubernetes client: %v", err)
os.Exit(1)
}
kedaNamespace, _ := k.GetNamespace()
color.Blue(fmt.Sprintf("Keda namespace: %s\n", kedaNamespace))
kClient, _ := v1alpha1.NewForConfig(client.Config)
scaledObjectList, _ := kClient.ScaledObjects("").List(context.Background(), v1.ListOptions{})
scaledJobList, _ := kClient.ScaledJobs("").List(context.Background(), v1.ListOptions{})
triggerAuthenticationList, _ := kClient.TriggerAuthentications("").List(context.Background(), v1.ListOptions{})
clusterTriggerAuthenticationsList, _ := kClient.ClusterTriggerAuthentications().List(context.Background(), v1.ListOptions{})
// Before uninstalling the Helm chart, we need to delete Keda resources
for _, scaledObject := range scaledObjectList.Items {
err := kClient.ScaledObjects(scaledObject.Namespace).Delete(context.Background(), scaledObject.Name, v1.DeleteOptions{})
if err != nil {
fmt.Printf("Error deleting scaledObject %s: %v\n", scaledObject.Name, err)
} else {
fmt.Printf("Deleted scaledObject %s in namespace %s\n", scaledObject.Name, scaledObject.Namespace)
}
}
for _, scaledJob := range scaledJobList.Items {
err := kClient.ScaledJobs(scaledJob.Namespace).Delete(context.Background(), scaledJob.Name, v1.DeleteOptions{})
if err != nil {
fmt.Printf("Error deleting scaledJob %s: %v\n", scaledJob.Name, err)
} else {
fmt.Printf("Deleted scaledJob %s in namespace %s\n", scaledJob.Name, scaledJob.Namespace)
}
}
for _, triggerAuthentication := range triggerAuthenticationList.Items {
err := kClient.TriggerAuthentications(triggerAuthentication.Namespace).Delete(context.Background(), triggerAuthentication.Name, v1.DeleteOptions{})
if err != nil {
fmt.Printf("Error deleting triggerAuthentication %s: %v\n", triggerAuthentication.Name, err)
} else {
fmt.Printf("Deleted triggerAuthentication %s in namespace %s\n", triggerAuthentication.Name, triggerAuthentication.Namespace)
}
}
for _, clusterTriggerAuthentication := range clusterTriggerAuthenticationsList.Items {
err := kClient.ClusterTriggerAuthentications().Delete(context.Background(), clusterTriggerAuthentication.Name, v1.DeleteOptions{})
if err != nil {
fmt.Printf("Error deleting clusterTriggerAuthentication %s: %v\n", clusterTriggerAuthentication.Name, err)
} else {
fmt.Printf("Deleted clusterTriggerAuthentication %s\n", clusterTriggerAuthentication.Name)
}
}
chartSpec := helmclient.ChartSpec{
ReleaseName: ReleaseName,
ChartName: fmt.Sprintf("%s/%s", RepoShortName, ChartName),
Namespace: namespace,
UpgradeCRDs: true,
Wait: false,
Timeout: 300,
}
// Uninstall the chart release.
// Note that helmclient.Options.Namespace should ideally match the namespace in chartSpec.Namespace.
if err := k.helm.UninstallRelease(&chartSpec); err != nil {
return err
}
return nil
}
func (k *Keda) AddAnalyzer(mergedMap *map[string]common.IAnalyzer) {
(*mergedMap)["ScaledObject"] = &ScaledObjectAnalyzer{}
}
func (k *Keda) GetAnalyzerName() []string {
return []string{
"ScaledObject",
}
}
func (k *Keda) GetNamespace() (string, error) {
releases, err := k.helm.ListDeployedReleases()
if err != nil {
return "", err
}
for _, rel := range releases {
if rel.Name == ReleaseName {
return rel.Namespace, nil
}
}
return "", status.Error(codes.NotFound, "keda release not found")
}
func (k *Keda) OwnsAnalyzer(analyzer string) bool {
for _, a := range k.GetAnalyzerName() {
if analyzer == a {
return true
}
}
return false
}
func (k *Keda) isFilterActive() bool {
activeFilters := viper.GetStringSlice("active_filters")
for _, filter := range k.GetAnalyzerName() {
for _, af := range activeFilters {
if af == filter {
return true
}
}
}
return false
}
func (k *Keda) isDeployed() bool {
kubecontext := viper.GetString("kubecontext")
kubeconfig := viper.GetString("kubeconfig")
client, err := kubernetes.NewClient(kubecontext, kubeconfig)
if err != nil {
// TODO: better error handling
color.Red("Error initialising kubernetes client: %v", err)
os.Exit(1)
}
groups, _, err := client.Client.Discovery().ServerGroupsAndResources()
if err != nil {
// TODO: better error handling
color.Red("Error initialising discovery client: %v", err)
os.Exit(1)
}
for _, group := range groups {
if group.Name == "keda.sh" {
return true
}
}
return false
}
func (k *Keda) IsActivate() bool {
return k.isFilterActive() && k.isDeployed()
}

View File

@@ -0,0 +1,193 @@
package keda
import (
"fmt"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
kedaSchema "github.com/kedacore/keda/v2/apis/keda/v1alpha1"
"github.com/kedacore/keda/v2/pkg/generated/clientset/versioned/typed/keda/v1alpha1"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type ScaledObjectAnalyzer struct{}
func (s *ScaledObjectAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
kClient, _ := v1alpha1.NewForConfig(a.Client.GetConfig())
kind := "ScaledObject"
apiDoc := kubernetes.K8sApiReference{
Kind: kind,
ApiVersion: kedaSchema.GroupVersion,
OpenapiSchema: a.OpenapiSchema,
}
list, err := kClient.ScaledObjects(a.Namespace).List(a.Context, metav1.ListOptions{})
if err != nil {
return nil, err
}
var preAnalysis = map[string]common.PreAnalysis{}
for _, so := range list.Items {
var failures []common.Failure
scaleTargetRef := so.Spec.ScaleTargetRef
if scaleTargetRef.Kind == "" {
scaleTargetRef.Kind = "Deployment"
}
var podInfo PodInfo
switch scaleTargetRef.Kind {
case "Deployment":
deployment, err := a.Client.GetClient().AppsV1().Deployments(so.Namespace).Get(a.Context, scaleTargetRef.Name, metav1.GetOptions{})
if err == nil {
podInfo = DeploymentInfo{deployment}
}
case "ReplicationController":
rc, err := a.Client.GetClient().CoreV1().ReplicationControllers(so.Namespace).Get(a.Context, scaleTargetRef.Name, metav1.GetOptions{})
if err == nil {
podInfo = ReplicationControllerInfo{rc}
}
case "ReplicaSet":
rs, err := a.Client.GetClient().AppsV1().ReplicaSets(so.Namespace).Get(a.Context, scaleTargetRef.Name, metav1.GetOptions{})
if err == nil {
podInfo = ReplicaSetInfo{rs}
}
case "StatefulSet":
ss, err := a.Client.GetClient().AppsV1().StatefulSets(so.Namespace).Get(a.Context, scaleTargetRef.Name, metav1.GetOptions{})
if err == nil {
podInfo = StatefulSetInfo{ss}
}
default:
failures = append(failures, common.Failure{
Text: fmt.Sprintf("ScaledObject uses %s as ScaleTargetRef which is not an option.", scaleTargetRef.Kind),
Sensitive: []common.Sensitive{},
})
}
if podInfo == nil {
doc := apiDoc.GetApiDocV2("spec.scaleTargetRef")
failures = append(failures, common.Failure{
Text: fmt.Sprintf("ScaledObject uses %s/%s as ScaleTargetRef which does not exist.", scaleTargetRef.Kind, scaleTargetRef.Name),
KubernetesDoc: doc,
Sensitive: []common.Sensitive{
{
Unmasked: scaleTargetRef.Name,
Masked: util.MaskString(scaleTargetRef.Name),
},
},
})
} else {
containers := len(podInfo.GetPodSpec().Containers)
for _, container := range podInfo.GetPodSpec().Containers {
for _, trigger := range so.Spec.Triggers {
if trigger.Type == "cpu" || trigger.Type == "memory" {
if container.Resources.Requests == nil || container.Resources.Limits == nil {
containers--
break
}
}
}
}
if containers <= 0 {
doc := apiDoc.GetApiDocV2("spec.scaleTargetRef.kind")
failures = append(failures, common.Failure{
Text: fmt.Sprintf("%s %s/%s does not have resource configured.", scaleTargetRef.Kind, so.Namespace, scaleTargetRef.Name),
KubernetesDoc: doc,
Sensitive: []common.Sensitive{
{
Unmasked: scaleTargetRef.Name,
Masked: util.MaskString(scaleTargetRef.Name),
},
},
})
}
evt, err := util.FetchLatestEvent(a.Context, a.Client, so.Namespace, so.Name)
if err != nil || evt == nil {
continue
}
if evt.Type != "Normal" {
failures = append(failures, common.Failure{
Text: evt.Message,
Sensitive: []common.Sensitive{
{
Unmasked: scaleTargetRef.Name,
Masked: util.MaskString(scaleTargetRef.Name),
},
},
})
}
}
if len(failures) > 0 {
preAnalysis[fmt.Sprintf("%s/%s", so.Namespace, so.Name)] = common.PreAnalysis{
ScaledObject: so,
FailureDetails: failures,
}
}
}
for key, value := range preAnalysis {
var currentAnalysis = common.Result{
Kind: kind,
Name: key,
Error: value.FailureDetails,
}
parent, _ := util.GetParent(a.Client, value.ScaledObject.ObjectMeta)
currentAnalysis.ParentObject = parent
a.Results = append(a.Results, currentAnalysis)
}
return a.Results, nil
}
type PodInfo interface {
GetPodSpec() corev1.PodSpec
}
type DeploymentInfo struct {
*appsv1.Deployment
}
func (d DeploymentInfo) GetPodSpec() corev1.PodSpec {
return d.Spec.Template.Spec
}
// define a structure for ReplicationController
type ReplicationControllerInfo struct {
*corev1.ReplicationController
}
func (rc ReplicationControllerInfo) GetPodSpec() corev1.PodSpec {
return rc.Spec.Template.Spec
}
// define a structure for ReplicaSet
type ReplicaSetInfo struct {
*appsv1.ReplicaSet
}
func (rs ReplicaSetInfo) GetPodSpec() corev1.PodSpec {
return rs.Spec.Template.Spec
}
// define a structure for StatefulSet
type StatefulSetInfo struct {
*appsv1.StatefulSet
}
// implement PodInfo for StatefulSetInfo
func (ss StatefulSetInfo) GetPodSpec() corev1.PodSpec {
return ss.Spec.Template.Spec
}

View File

@@ -1,79 +0,0 @@
/*
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())
}

View File

@@ -1,16 +0,0 @@
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,6 +9,16 @@ import (
"google.golang.org/grpc/status"
)
const (
notUsedBucket = ""
notUsedRegion = ""
notUsedEndpoint = ""
notUsedStorageAcc = ""
notUsedContainerName = ""
notUsedProjectId = ""
notUsedInsecure = false
)
func (h *handler) AddConfig(ctx context.Context, i *schemav1.AddConfigRequest) (*schemav1.AddConfigResponse, error,
) {
@@ -23,11 +33,11 @@ func (h *handler) AddConfig(ctx context.Context, i *schemav1.AddConfigRequest) (
switch i.Cache.GetCacheType().(type) {
case *schemav1.Cache_AzureCache:
remoteCache, err = cache.NewCacheProvider("azure", "", "", i.Cache.GetAzureCache().StorageAccount, i.Cache.GetAzureCache().ContainerName, "")
remoteCache, err = cache.NewCacheProvider("azure", notUsedBucket, notUsedRegion, notUsedEndpoint, i.Cache.GetAzureCache().StorageAccount, i.Cache.GetAzureCache().ContainerName, notUsedProjectId, notUsedInsecure)
case *schemav1.Cache_S3Cache:
remoteCache, err = cache.NewCacheProvider("s3", i.Cache.GetS3Cache().BucketName, i.Cache.GetS3Cache().Region, "", "", "")
remoteCache, err = cache.NewCacheProvider("s3", i.Cache.GetS3Cache().BucketName, i.Cache.GetS3Cache().Region, i.Cache.GetS3Cache().Endpoint, notUsedStorageAcc, notUsedContainerName, notUsedProjectId, i.Cache.GetS3Cache().Insecure)
case *schemav1.Cache_GcsCache:
remoteCache, err = cache.NewCacheProvider("gcs", i.Cache.GetGcsCache().BucketName, i.Cache.GetGcsCache().Region, "", "", i.Cache.GetGcsCache().GetProjectId())
remoteCache, err = cache.NewCacheProvider("gcs", i.Cache.GetGcsCache().BucketName, i.Cache.GetGcsCache().Region, notUsedEndpoint, notUsedStorageAcc, notUsedContainerName, i.Cache.GetGcsCache().GetProjectId(), notUsedInsecure)
default:
return resp, status.Error(codes.InvalidArgument, "Invalid cache configuration")
}

View File

@@ -33,15 +33,6 @@ import (
var anonymizePattern = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+[]{}|;':\",./<>?")
func SliceContainsString(slice []string, s string) bool {
for _, item := range slice {
if item == s {
return true
}
}
return false
}
func GetParent(client *kubernetes.Client, meta metav1.ObjectMeta) (string, bool) {
if meta.OwnerReferences != nil {
for _, owner := range meta.OwnerReferences {
@@ -54,7 +45,7 @@ func GetParent(client *kubernetes.Client, meta metav1.ObjectMeta) (string, bool)
if rs.OwnerReferences != nil {
return GetParent(client, rs.ObjectMeta)
}
return "ReplicaSet/" + rs.Name, false
return "ReplicaSet/" + rs.Name, true
case "Deployment":
dep, err := client.GetClient().AppsV1().Deployments(meta.Namespace).Get(context.Background(), owner.Name, metav1.GetOptions{})
@@ -64,7 +55,7 @@ func GetParent(client *kubernetes.Client, meta metav1.ObjectMeta) (string, bool)
if dep.OwnerReferences != nil {
return GetParent(client, dep.ObjectMeta)
}
return "Deployment/" + dep.Name, false
return "Deployment/" + dep.Name, true
case "StatefulSet":
sts, err := client.GetClient().AppsV1().StatefulSets(meta.Namespace).Get(context.Background(), owner.Name, metav1.GetOptions{})
@@ -74,7 +65,7 @@ func GetParent(client *kubernetes.Client, meta metav1.ObjectMeta) (string, bool)
if sts.OwnerReferences != nil {
return GetParent(client, sts.ObjectMeta)
}
return "StatefulSet/" + sts.Name, false
return "StatefulSet/" + sts.Name, true
case "DaemonSet":
ds, err := client.GetClient().AppsV1().DaemonSets(meta.Namespace).Get(context.Background(), owner.Name, metav1.GetOptions{})
@@ -84,7 +75,7 @@ func GetParent(client *kubernetes.Client, meta metav1.ObjectMeta) (string, bool)
if ds.OwnerReferences != nil {
return GetParent(client, ds.ObjectMeta)
}
return "DaemonSet/" + ds.Name, false
return "DaemonSet/" + ds.Name, true
case "Ingress":
ds, err := client.GetClient().NetworkingV1().Ingresses(meta.Namespace).Get(context.Background(), owner.Name, metav1.GetOptions{})
@@ -94,7 +85,7 @@ func GetParent(client *kubernetes.Client, meta metav1.ObjectMeta) (string, bool)
if ds.OwnerReferences != nil {
return GetParent(client, ds.ObjectMeta)
}
return "Ingress/" + ds.Name, false
return "Ingress/" + ds.Name, true
case "MutatingWebhookConfiguration":
mw, err := client.GetClient().AdmissionregistrationV1().MutatingWebhookConfigurations().Get(context.Background(), owner.Name, metav1.GetOptions{})
@@ -104,7 +95,7 @@ func GetParent(client *kubernetes.Client, meta metav1.ObjectMeta) (string, bool)
if mw.OwnerReferences != nil {
return GetParent(client, mw.ObjectMeta)
}
return "MutatingWebhook/" + mw.Name, false
return "MutatingWebhook/" + mw.Name, true
case "ValidatingWebhookConfiguration":
vw, err := client.GetClient().AdmissionregistrationV1().ValidatingWebhookConfigurations().Get(context.Background(), owner.Name, metav1.GetOptions{})
@@ -114,11 +105,11 @@ func GetParent(client *kubernetes.Client, meta metav1.ObjectMeta) (string, bool)
if vw.OwnerReferences != nil {
return GetParent(client, vw.ObjectMeta)
}
return "ValidatingWebhook/" + vw.Name, false
return "ValidatingWebhook/" + vw.Name, true
}
}
}
return meta.Name, false
return "", false
}
func RemoveDuplicates(slice []string) ([]string, []string) {
@@ -183,7 +174,8 @@ func GetCacheKey(provider string, language string, sEnc string) string {
func GetPodListByLabels(client k.Interface,
namespace string,
labels map[string]string) (*v1.PodList, error) {
labels map[string]string,
) (*v1.PodList, error) {
pods, err := client.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{
LabelSelector: metav1.FormatLabelSelector(&metav1.LabelSelector{
MatchLabels: labels,
@@ -207,7 +199,7 @@ func FileExists(path string) (bool, error) {
}
func EnsureDirExists(dir string) error {
err := os.MkdirAll(dir, 0755)
err := os.MkdirAll(dir, 0o755)
if errors.Is(err, os.ErrExist) {
return nil
@@ -241,3 +233,31 @@ func LabelsIncludeAny(predefinedSelector, Labels map[string]string) bool {
return false
}
func FetchLatestEvent(ctx context.Context, kubernetesClient *kubernetes.Client, namespace string, name string) (*v1.Event, error) {
// get the list of events
events, err := kubernetesClient.GetClient().CoreV1().Events(namespace).List(ctx,
metav1.ListOptions{
FieldSelector: "involvedObject.name=" + name,
})
if err != nil {
return nil, err
}
// find most recent event
var latestEvent *v1.Event
for _, event := range events.Items {
if latestEvent == nil {
// this is required, as a pointer to a loop variable would always yield the latest value in the range
e := event
latestEvent = &e
}
if event.LastTimestamp.After(latestEvent.LastTimestamp.Time) {
// this is required, as a pointer to a loop variable would always yield the latest value in the range
e := event
latestEvent = &e
}
}
return latestEvent, nil
}

View File

@@ -26,29 +26,6 @@ import (
"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"
@@ -105,7 +82,7 @@ func TestGetParent(t *testing.T) {
}{
{
kind: "Unknown",
expectedOutput: ownerName,
expectedOutput: "",
},
{
kind: "ReplicaSet",
@@ -178,8 +155,12 @@ func TestGetParent(t *testing.T) {
},
}
output, ok := GetParent(&kubeClient, meta)
if meta.OwnerReferences[0].Name != "" {
require.Equal(t, true, ok)
} else {
require.Equal(t, false, ok)
}
require.Equal(t, tt.expectedOutput, output)
require.Equal(t, false, ok)
})
}
}
@@ -409,6 +390,7 @@ func TestGetPodListByLabels(t *testing.T) {
})
}
}
func TestFileExists(t *testing.T) {
tests := []struct {
filePath string

View File

@@ -12,37 +12,30 @@
],
"automerge": true,
"automergeType": "pr",
"schedule": [
"at any time"
],
"platformAutomerge": true,
"packageRules": [
{
"matchPackageNames": ["^github.com/Azure/azure-sdk-for-go/"],
"matchPackageNames": ["azure-sdk-for-go"],
"enabled": true,
"group": "azure-group"
"groupName": "azure-group"
},
{
"matchPackageNames": ["^github.com/prometheus/"],
"matchPackageNames": ["prometheus"],
"enabled": true,
"group": "prometheus-group"
"groupName": "prometheus-group"
},
{
"matchPackageNames": ["^k8s.io/", "^sigs.k8s.io/"],
"matchPackageNames": ["k8s.io", "sigs.k8s.io"],
"enabled": true,
"groupName": "kubernetes-group"
},
{
"matchPackageNames": ["^github.com/golang/","^golang.org"],
"matchPackageNames": ["golang"],
"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"],
"allowedVersions": "<1"
"groupName": "golang-group"
},
{
"matchUpdateTypes": ["minor", "patch"],