Compare commits

...

125 Commits

Author SHA1 Message Date
Alex Jones
65632b497b chore: resolved mod
Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
2024-02-08 17:36:21 +00:00
Sahil Badla
9952a5d292 Merge branch 'main' into rest-api
Signed-off-by: Sahil Badla <146279034+sbadla1@users.noreply.github.com>
2024-01-16 10:25:07 -08:00
Sahil Badla
d9c66b4852 feat: enable rest/http support
Signed-off-by: Sahil Badla <sahil_badla@intuit.com>
2024-01-16 10:17:38 -08:00
Sahil Badla
db97330659 feat: enable rest/http support
Signed-off-by: Sahil Badla <sahil_badla@intuit.com>
2024-01-16 10:15:08 -08:00
Sahil Badla
c2e01ea9b6 feat: enable rest/http support
Signed-off-by: Sahil Badla <sahil_badla@intuit.com>
2024-01-16 10:07:35 -08:00
Sahil Badla
5ea1b35cd5 feat: enable REST/http api support
Signed-off-by: Sahil Badla <sahil_badla@intuit.com>
2024-01-16 10:07:31 -08:00
renovate[bot]
4de1bbd6f7 fix(deps): update module github.com/sashabaranov/go-openai to v1.18.2 (#874)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-16 12:43:49 +00:00
renovate[bot]
81d660447d fix(deps): update module gopkg.in/yaml.v2 to v3 (#866)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-16 09:56:21 +00:00
Philip Miglinci
a34f5dea69 ci: replace deprecated brew config (#873)
tap got replaces by repository and is now deprecated.

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

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


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

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

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

* chore: tidied up a bit

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

* chore: updated go mod

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

* chore: const prompt

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

* chore: updated based on comments feedback

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

* chore: updated enum

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

* chore: updated enum

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

---------

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

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

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

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

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

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

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

* review: feedback cycle 1

Simplify ConfigValidate prompt and add comments.

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

* review: feedback cycle 2

Add Prometheus configuration discovery to integration activate command.

Also improve logging to make this more clear to users.

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

---------

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

Signed-off-by: bwplotka <bwplotka@gmail.com>
2024-01-05 22:30:34 +01:00
github-actions[bot]
6d29fcf294 chore(main): release 0.3.25 (#812)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-01-05 07:35:44 +00:00
Bartlomiej Plotka
e7d41496dd feat: added Google GenAI client; simplified IAI/clients API surface. (#829)
* refactor: Simplified IAI; made caching and processing consisent.


Signed-off-by: bwplotka <bwplotka@gmail.com>

* feat: Added Google AI API e.g. for Gemini models.

Signed-off-by: bwplotka <bwplotka@gmail.com>

---------

Signed-off-by: bwplotka <bwplotka@gmail.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
Co-authored-by: Thomas Schuetz <38893055+thschue@users.noreply.github.com>
2024-01-05 06:53:36 +01:00
renovate[bot]
e78ff05419 fix(deps): update module github.com/aws/aws-sdk-go to v1.49.15 (#835)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-05 06:25:05 +01:00
Thomas Schuetz
d661dfe585 Merge branch 'main' into rest-api 2024-01-05 06:17:30 +01:00
renovate[bot]
105a239d94 fix(deps): update module google.golang.org/api to v0.155.0 (#836)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-05 06:16:07 +01:00
Sahil Badla
4ad9c49570 feat: grpc-gateway impl
Signed-off-by: Sahil Badla <sahil_badla@intuit.com>
2024-01-04 11:15:01 -08:00
renovate[bot]
4de989c803 fix(deps): update module golang.org/x/term to v0.16.0 (#831)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-04 19:56:11 +01:00
Alex Jones
a7e9b486ba chore: lint fixes (#833)
* chore: added basic server startup test

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

* chore: refactored wg.add move

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

---------

Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
2024-01-04 17:03:32 +00:00
Alex Jones
a77426593d chore: remove code cov (#832)
Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
2024-01-04 16:47:39 +00:00
renovate[bot]
526e22f88b fix(deps): update module github.com/aws/aws-sdk-go to v1.49.14 (#822)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-04 08:48:08 +00:00
renovate[bot]
4314804ca7 fix(deps): update module github.com/sashabaranov/go-openai to v1.17.10 (#824)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-04 06:48:12 +01:00
Sahil Badla
f1be5055db grpc-gateway changes
Signed-off-by: Sahil Badla <sahil_badla@intuit.com>
2024-01-03 16:53:09 -08:00
Bartlomiej Plotka
035348d8a0 fix: no explain case, improved readability. (#825)
Signed-off-by: bwplotka <bwplotka@gmail.com>
2024-01-03 17:59:28 +01:00
renovate[bot]
70c68929d8 chore(deps): update anchore/sbom-action action to v0.15.2 (#823)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-03 13:10:43 +00:00
Debasish Biswas
b17fd7c986 deps: go module bump to fix CVE: GHSA-45x7-px36-x8w8 & GHSA-7ww5-4wqc-m92c (#810)
Signed-off-by: debasishbsws <debasishbsws.dev@gmail.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2023-12-29 14:36:11 +00:00
renovate[bot]
2f0f2dfa8a chore(deps): pin codecov/codecov-action action to eaaf4be (#820)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-29 14:20:29 +00:00
Alex Jones
3e7cea7bd3 chore: added basic server startup test (#817)
Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
2023-12-29 14:15:32 +00:00
Alex Jones
fcd29a547d feat: code_cov badge (#821)
Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
2023-12-29 14:08:06 +00:00
Alex Jones
3d0ba3e78c feat: coverage reports (#819)
* Update test.yaml

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

* Update test.yaml

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

---------

Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
2023-12-29 14:05:27 +00:00
renovate[bot]
91613baa5c fix(deps): update module github.com/aws/aws-sdk-go to v1.49.12 (#813)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-29 08:48:36 +00:00
renovate[bot]
6eb8f6793e fix(deps): update module github.com/prometheus/client_golang to v1.18.0 (#814)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-29 08:24:31 +00:00
renovate[bot]
e5cc4a28cb fix(deps): update module github.com/aws/aws-sdk-go to v1.49.10 (#811)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-27 10:32:16 +01:00
github-actions[bot]
eac9f07abf chore(main): release 0.3.24 (#781)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-12-24 07:39:44 +00:00
renovate[bot]
130e4c2efd fix(deps): update module github.com/aws/aws-sdk-go to v1.49.9 (#808)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-23 08:15:53 +00:00
renovate[bot]
93b5ca1985 fix(deps): update module github.com/aws/aws-sdk-go to v1.49.8 (#807)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-22 12:57:46 +00:00
renovate[bot]
aa057565b5 fix(deps): update module github.com/azure/azure-sdk-for-go/sdk/storage/azblob to v1.2.1 (#801)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-22 07:59:01 +00:00
renovate[bot]
13d64a5875 fix(deps): update module github.com/sashabaranov/go-openai to v1.17.9 (#772)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-21 17:00:38 +00:00
Akhil Rane
03b63befa2 fix: lowercase logs before running regex matching in LogAnalyzer (#794)
Signed-off-by: Akhil Rane <akhil131192@gmail.com>
2023-12-21 16:59:13 +00:00
renovate[bot]
3c6c7597e0 fix(deps): update module github.com/aws/aws-sdk-go to v1.49.7 (#804)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-21 16:08:02 +00:00
renovate[bot]
71f36bdb0b fix(deps): update module github.com/aquasecurity/trivy-operator to v0.17.1 (#780)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-21 16:04:07 +00:00
renovate[bot]
d6fb648e23 chore(deps): update actions/upload-artifact action to v4 (#806)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-21 12:15:52 +00:00
fossabot
343aec8f04 feat: Add license scan report and status (#796)
Signed off by: fossabot <badges@fossa.com>

Co-authored-by: Thomas Schuetz <38893055+thschue@users.noreply.github.com>
2023-12-21 13:11:44 +01:00
renovate[bot]
78f7f2ba85 fix(deps): update module google.golang.org/api to v0.154.0 (#779)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-21 12:06:30 +00:00
renovate[bot]
a8e1932122 fix(deps): update module helm.sh/helm/v3 to v3.13.3 (#803)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-21 13:04:37 +01:00
renovate[bot]
390f309088 fix(deps): update module cloud.google.com/go/storage to v1.36.0 (#805)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-21 10:37:14 +00:00
renovate[bot]
5d54c3f840 fix(deps): update module google.golang.org/grpc to v1.60.1 (#790)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-21 10:12:32 +00:00
renovate[bot]
4a7bad313b fix(deps): update module github.com/mittwald/go-helm-client to v0.12.5 (#802)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-21 10:00:54 +00:00
renovate[bot]
be4b0bb3c2 chore(deps): update google-github-actions/release-please-action action to v4.0.2 (#800)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-20 17:14:55 +01:00
renovate[bot]
1b386f64f2 fix(deps): update module github.com/aws/aws-sdk-go to v1.49.6 (#783)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-20 16:08:33 +00:00
renovate[bot]
8dea6170a2 fix(deps): update module github.com/spf13/viper to v1.18.2 (#787)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-20 16:04:32 +00:00
Swastik Gour
928b39a728 fix: added the ability to set the trivy variables by the user (#797)
Signed-off-by: swastik959 <Sswastik959@gmail.com>
Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2023-12-20 15:57:28 +00:00
Alex Jones
c23f24de2e feat: version upgrade to 1.21 (#798)
* feat: version upgrade to 1.21

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

* feat: updated dockerfile

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

* feat: updated dockerfile

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

---------

Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
2023-12-20 15:51:27 +00:00
Akhil Rane
ff4aaf7c32 feat: add last termination state when pod is in CrashloopBackoff (#792)
Signed-off-by: Akhil Rane <akhil131192@gmail.com>
Co-authored-by: Akhil Rane <akhil_rane@intuit.com>
2023-12-20 12:57:17 +00:00
renovate[bot]
2c28c555cf chore(deps): update google-github-actions/release-please-action action to v4 (#782)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-08 08:55:55 +00:00
renovate[bot]
d00ed33678 chore(deps): update actions/setup-go action to v5 (#788)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-07 12:46:57 +00:00
renovate[bot]
6473a2b532 chore(deps): update anchore/sbom-action action to v0.15.1 (#784)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-06 09:24:38 +00:00
Xiangkun Yin
1d196286b7 refactor: replace rest client with controller-runtime clientset for Trivy analyzers (#776)
* refactor: replace rest client with controller-runtime clientset for Trivy analyzers

Signed-off-by: ptyin <peteryin1604@gmail.com>

* refactor: remove rest client

Signed-off-by: ptyin <peteryin1604@gmail.com>

---------

Signed-off-by: ptyin <peteryin1604@gmail.com>
2023-11-29 15:13:38 +00:00
Matthis
71ae5a7301 doc: use grpcurl instead curl (#773)
Signed-off-by: Matthis Holleville <matthish29@gmail.com>
2023-11-25 16:42:49 +00:00
github-actions[bot]
eb32a0f2bc chore(main): release 0.3.23 (#769)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-11-24 14:01:28 +00:00
Aris Boutselis
ec08cac214 feat: add Gateway analysers (#764)
* feat: add GatewayClass analyser

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

* chore: add a valid GW class object

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

* feat: add gw analyzer and switch to controller-runtime client

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

* chore: add unit tests for gw analyser

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

* chore: replace constants with condition status

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

* feat: add httproute analyzer

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

* feat: add HTTPRoute individual tests.

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

* docs: add analyzers

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

---------

Signed-off-by: Aris Boutselis <arisboutselis08@gmail.com>
Signed-off-by: Aris Boutselis <aris.boutselis@senseon.io>
Co-authored-by: Aris Boutselis <arisboutselis08@gmail.com>
2023-11-24 10:09:54 +00:00
renovate[bot]
dca5b4710d fix(deps): update module github.com/mittwald/go-helm-client to v0.12.4 (#767)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-23 08:14:48 +00:00
renovate[bot]
b1c791a396 fix(deps): update module github.com/aws/aws-sdk-go to v1.48.3 (#768)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-23 08:02:35 +00:00
github-actions[bot]
b780105807 chore(main): release 0.3.22 (#748)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-11-22 09:32:57 +00:00
renovate[bot]
16469c01c9 fix(deps): update module github.com/aws/aws-sdk-go to v1.48.1 (#766)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-21 09:23:08 +00:00
renovate[bot]
cf1e243708 chore(deps): update anchore/sbom-action action to v0.15.0 (#765)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-20 19:07:58 +00:00
renovate[bot]
b5853de8a6 chore(deps): update docker/build-push-action digest to 4a13e50 (#760)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-20 18:54:37 +00:00
renovate[bot]
58d182e94f fix(deps): update module cloud.google.com/go/storage to v1.35.1 (#762)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-20 13:49:32 +00:00
Jesang Myung
5b27c3e352 fix: cover more error reason messages (#759)
* fix: cover more error reasons (#758)

Signed-off-by: Jesang Myung <jesang.myung@gmail.com>

* fix: refactoring for simplify conditions

Signed-off-by: Jesang Myung <jesang.myung@gmail.com>

---------

Signed-off-by: Jesang Myung <jesang.myung@gmail.com>
2023-11-20 13:01:04 +00:00
renovate[bot]
3e3f6a903a fix(deps): update module google.golang.org/api to v0.151.0 (#763)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-20 12:42:13 +00:00
Matthis
12146bf356 feat: rework cache package - add gcs cache - add cache purge command (#750)
* feat: rework cache pkg

Signed-off-by: Matthis Holleville <matthish29@gmail.com>

* feat: Completion of cache pkg rework. Added cache purge command.

Signed-off-by: Matthis Holleville <matthish29@gmail.com>

* doc: add purgin command note

Signed-off-by: Matthis Holleville <matthish29@gmail.com>

* fix: disable cache if noCache is set

Signed-off-by: Matthis Holleville <matthish29@gmail.com>

* feat: improve GetCacheConfiguration lisibility & transform add method to addOrUpdate

Signed-off-by: Matthis Holleville <matthish29@gmail.com>

* feat: transform server mode to work with new cache configuration

Signed-off-by: Matthis Holleville <matthish29@gmail.com>

* fix: use 'switch' instead 'if' to evaluate Cache from grpc

Signed-off-by: Matthis Holleville <matthish29@gmail.com>

* feat: add mutually exclusive flags for command options

Signed-off-by: Matthis Holleville <matthish29@gmail.com>

* doc: update readme.md

Signed-off-by: Matthis Holleville <matthish29@gmail.com>

* feat: return err on bucket creation failed

Signed-off-by: Matthis Holleville <matthish29@gmail.com>

* feat: update dependencies

Signed-off-by: Matthis Holleville <matthish29@gmail.com>

---------

Signed-off-by: Matthis Holleville <matthish29@gmail.com>
Signed-off-by: Matthis <matthish29@gmail.com>
2023-11-18 22:08:38 +01:00
renovate[bot]
beaa53251c fix(deps): update module github.com/sashabaranov/go-openai to v1.17.8 (#761)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-18 20:23:44 +00:00
renovate[bot]
e2bb567d2f fix(deps): update module github.com/aws/aws-sdk-go to v1.48.0 (#754)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-18 20:14:37 +00:00
renovate[bot]
24132c2d87 fix(deps): update kubernetes packages to v0.28.4 (#756)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-16 07:02:15 -05:00
Alex Jones
11e568c6c2 Update README.md (#757)
Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
2023-11-16 06:47:27 -05:00
renovate[bot]
9971699fcf fix(deps): update module github.com/sashabaranov/go-openai to v1.17.7 (#753)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-15 21:10:29 +00:00
renovate[bot]
531fa79ed6 fix(deps): update module github.com/aws/aws-sdk-go to v1.47.11 (#752)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-15 06:55:39 -05:00
renovate[bot]
2aa31bc66d fix(deps): update module github.com/aws/aws-sdk-go to v1.47.10 (#751)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-13 20:14:47 -05:00
renovate[bot]
84df3640bc fix(deps): update module github.com/sashabaranov/go-openai to v1.17.6 (#749)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-13 11:51:11 -05:00
Johannes Kleinlercher
73682717ed fix: show trivy as active when activated with --no-install flag (#675)
Signed-off-by: Johannes Kleinlercher <johannes@kleinlercher.at>
Signed-off-by: Thomas Schuetz <38893055+thschue@users.noreply.github.com>
Co-authored-by: Thomas Schuetz <38893055+thschue@users.noreply.github.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2023-11-13 11:10:13 -05:00
renovate[bot]
45312788c3 fix(deps): update module github.com/aquasecurity/trivy-operator to v0.16.4 (#676)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-12 16:52:11 -05:00
github-actions[bot]
c78c4f0cb6 chore(main): release 0.3.21 (#735)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-11-12 16:09:05 -05:00
Alex Jones
d365886753 feat: log analyzer (#744)
* feat: wip log analyzer

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

* chore: turns off log by default

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

* chore: turns off log by default

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

---------

Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
Co-authored-by: Thomas Schuetz <38893055+thschue@users.noreply.github.com>
2023-11-12 16:00:43 -05:00
renovate[bot]
4408110b1a chore(deps): pin google-github-actions/release-please-action action to db8f2c6 (#747)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-12 21:47:22 +01:00
Thomas Schuetz
c4925b2170 chore: pin release-please version (#746)
Signed-off-by: Thomas Schuetz <thomas.schuetz@t-sc.eu>
Signed-off-by: Thomas Schuetz <38893055+thschue@users.noreply.github.com>
2023-11-12 21:39:38 +01:00
Thomas Schuetz
66ebb88efe chore: enable automerge for renovate (#745)
Signed-off-by: Thomas Schuetz <thomas.schuetz@t-sc.eu>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2023-11-12 21:38:47 +01:00
Tsadok Levi Firseck
1a554ae840 Update pod.go - added handling of CreateContainerConfigError (#639)
Added handling of CreateContainerConfigError error

Signed-off-by: Tsadok Levi Firseck <tsadoklf@gmail.com>
Co-authored-by: Aris Boutselis <aris.boutselis@senseon.io>
Co-authored-by: Thomas Schuetz <38893055+thschue@users.noreply.github.com>
2023-11-12 21:24:41 +01:00
renovate[bot]
19e502a841 fix(deps): update module github.com/spf13/cobra to v1.8.0 (#732)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-12 21:20:00 +01:00
renovate[bot]
fdb2934e8f chore(deps): update google-github-actions/release-please-action digest to db8f2c6 (#736)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-12 20:54:56 +01:00
renovate[bot]
45ebad7b4d fix(deps): update module github.com/aws/aws-sdk-go to v1.47.9 (#743)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-11 05:53:31 -06:00
renovate[bot]
6a665f05d7 fix(deps): update module helm.sh/helm/v3 to v3.13.2 (#740)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 14:53:44 -06:00
renovate[bot]
d359caaab6 fix(deps): update module github.com/aws/aws-sdk-go to v1.47.8 (#741)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 14:37:54 -06:00
Pavel Sturc
9dadd186c8 feat: auth remove: add -b flag (#711)
* feat: auth remove: add -b flag

Signed-off-by: Pavel Sturc <psturc@redhat.com>

* fix: address comment

Signed-off-by: Pavel Sturc <psturc@redhat.com>

---------

Signed-off-by: Pavel Sturc <psturc@redhat.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
2023-11-10 14:15:24 -06:00
renovate[bot]
3bff9cbe7b fix(deps): update module github.com/sashabaranov/go-openai to v1.17.5 (#742)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 14:14:34 -06:00
renovate[bot]
05f444dec1 fix(deps): update module github.com/schollz/progressbar/v3 to v3.14.1 (#738)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-09 11:29:35 -06:00
renovate[bot]
bb21ce80c7 fix(deps): update module github.com/aws/aws-sdk-go to v1.47.6 (#728)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-08 16:25:43 -06:00
renovate[bot]
48486e9627 fix(deps): update module github.com/sashabaranov/go-openai to v1.17.3 (#737)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-08 07:33:10 -06:00
Ajay Kemparaj
120027e3cb deps: bump docker fixes CVE GHSA-jq35-85cj-fj4p (#733)
Signed-off-by: ajayk <ajaykemparaj@gmail.com>
2023-11-07 15:47:39 -06:00
renovate[bot]
9c518badf5 chore(deps): update google-github-actions/release-please-action digest to 4c5670f (#721)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-06 14:15:22 +01:00
renovate[bot]
8ab26d96ce fix(deps): update module github.com/fatih/color to v1.16.0 (#734)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-06 14:13:00 +01:00
github-actions[bot]
4978fe2523 chore(main): release 0.3.20 (#727)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-11-05 20:22:00 +00:00
DKuroczko
ccef7f6170 feat: amazonsagemaker AI provider (#731)
* feat(amazonsagemaker): Add AmazonSageMaker AI provider

Co-authored-by: NAME 18630245+zaremb@users.noreply.github.com
Signed-off-by: Damian Kuroczko <7778327+dkuroczk@users.noreply.github.com>

* feat(amazonsagemaker): Add AmazonSageMaker AI provider

Co-authored-by: Mateusz Zaremba <18630245+zaremb@users.noreply.github.com>
Signed-off-by: Damian Kuroczko <7778327+dkuroczk@users.noreply.github.com>

* feat(auth): add top p and max tokens to auth and use them in sagemaker backend

Signed-off-by: Mateusz Zaremba <18630245+zaremb@users.noreply.github.com>

* feat: Updates SageMaker docs, validate topP, ident

Signed-off-by: Damian Kuroczko <7778327+dkuroczk@users.noreply.github.com>

* feat: list of passwordlessProviders

Signed-off-by: Damian Kuroczko <7778327+dkuroczk@users.noreply.github.com>

* feat: returns err

Signed-off-by: Damian Kuroczko <7778327+dkuroczk@users.noreply.github.com>

* fix: remove log.Fatal(err)

Signed-off-by: Damian Kuroczko <7778327+dkuroczk@users.noreply.github.com>

---------

Signed-off-by: Damian Kuroczko <7778327+dkuroczk@users.noreply.github.com>
Signed-off-by: Mateusz Zaremba <18630245+zaremb@users.noreply.github.com>
Co-authored-by: Mateusz Zaremba <18630245+zaremb@users.noreply.github.com>
2023-11-05 20:03:40 +00:00
renovate[bot]
188a8a2cd5 chore(deps): update amannn/action-semantic-pull-request action to v5.4.0 (#729)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-04 21:40:40 +00:00
Ali
37721b5dd7 fix: ensure ingress HTTP rule exists to prevent panic (#726)
Signed-off-by: Ali Harris <aliharriss1995@gmail.com>
Co-authored-by: Aris Boutselis <aris.boutselis@senseon.io>
2023-11-03 14:02:51 +00:00
renovate[bot]
16b229d547 fix(deps): update module github.com/azure/azure-sdk-for-go/sdk/storage/azblob to v1.2.0 (#723)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-03 10:54:06 +00:00
renovate[bot]
901c5ec188 fix(deps): update module google.golang.org/grpc to v1.59.0 (#713)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-03 10:33:59 +00:00
renovate[bot]
40133adaed fix(deps): update module helm.sh/helm/v3 to v3.13.1 (#706)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-03 08:30:26 +00:00
renovate[bot]
0136b8f543 fix(deps): update module github.com/aws/aws-sdk-go to v1.47.1 (#724)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-02 15:42:17 +00:00
67 changed files with 5092 additions and 1062 deletions

View File

@@ -13,7 +13,7 @@ on:
- "**.md"
env:
GO_VERSION: "~1.20"
GO_VERSION: "~1.21"
IMAGE_NAME: "k8sgpt"
defaults:
run:
@@ -77,7 +77,7 @@ jobs:
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3
- name: Build Docker Image
uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5
uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5
with:
context: .
platforms: linux/amd64
@@ -96,7 +96,7 @@ jobs:
outputs: type=docker,dest=/tmp/${{ env.IMAGE_NAME }}-image.tar
- name: Upload image as artifact
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3
uses: actions/upload-artifact@1eb3cb2b3e0f29609092a73eb033bb759a334595 # v4
with:
name: ${{ env.IMAGE_NAME }}-image.tar
path: /tmp/${{ env.IMAGE_NAME }}-image.tar
@@ -129,7 +129,7 @@ jobs:
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3
- name: Build Docker Image
uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5
uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5
with:
context: .
file: ./container/Dockerfile

View File

@@ -24,8 +24,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- uses: google-github-actions/release-please-action@ca6063f4ed81b55db15b8c42d1b6f7925866342d # v3
- uses: google-github-actions/release-please-action@cc61a07e2da466bebbc19b3a7dd01d6aecb20d1e # v4.0.2
id: release
with:
command: manifest
@@ -45,11 +45,11 @@ jobs:
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5
with:
go-version: '1.20'
go-version: '1.21'
- name: Download Syft
uses: anchore/sbom-action/download-syft@78fc58e266e87a38d4194b2137a3d4e9bcaf7ca1 # v0.14.3
uses: anchore/sbom-action/download-syft@c7f031d9249a826a082ea14c79d3b686a51d485a # v0.15.3
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5
with:
@@ -90,7 +90,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker Image
uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5
uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5
with:
context: .
file: ./container/Dockerfile
@@ -104,7 +104,7 @@ jobs:
cache-to: type=gha,scope=${{ github.ref_name }}-${{ env.IMAGE_TAG }}
- name: Generate SBOM
uses: anchore/sbom-action@78fc58e266e87a38d4194b2137a3d4e9bcaf7ca1 # v0.14.3
uses: anchore/sbom-action@c7f031d9249a826a082ea14c79d3b686a51d485a # v0.15.3
with:
image: ${{ env.IMAGE_TAG }}
artifact-name: sbom-${{ env.IMAGE_NAME }}
@@ -114,4 +114,4 @@ jobs:
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
with:
tag_name: ${{ needs.release-please.outputs.tag_name }}
files: ./sbom-${{ env.IMAGE_NAME }}.spdx.json
files: ./sbom-${{ env.IMAGE_NAME }}.spdx.json

View File

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

View File

@@ -9,7 +9,7 @@ on:
- main
env:
GO_VERSION: "~1.20"
GO_VERSION: "~1.21"
jobs:
build:
@@ -19,13 +19,9 @@ jobs:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- name: Set up Go
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Unit Test
run: make test
# - name: Fmt Test
# run: fmtFiles=$(make fmt); if [ "$fmtFiles" != "" ];then exit 1; fi
- name: Run test
run: go test ./...

View File

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

View File

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

View File

@@ -1,5 +1,214 @@
# Changelog
## [0.3.26](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.25...v0.3.26) (2024-01-14)
### Features
* initial Prometheus analyzers ([#855](https://github.com/k8sgpt-ai/k8sgpt/issues/855)) ([45fa827](https://github.com/k8sgpt-ai/k8sgpt/commit/45fa827c046b91d901a08bec1a892d9c0917f350))
* interactive mode ([#854](https://github.com/k8sgpt-ai/k8sgpt/issues/854)) ([9da75e0](https://github.com/k8sgpt-ai/k8sgpt/commit/9da75e02bc17146898377e4f90b7f59c5a8e0eee))
* unify aiClientName const for all providers ([#848](https://github.com/k8sgpt-ai/k8sgpt/issues/848)) ([5c17c24](https://github.com/k8sgpt-ai/k8sgpt/commit/5c17c240550609d9fb7771fe67fe1ab19660b4da))
### Bug Fixes
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.16 ([#847](https://github.com/k8sgpt-ai/k8sgpt/issues/847)) ([ce4910b](https://github.com/k8sgpt-ai/k8sgpt/commit/ce4910bc5d064f80076877d7a096fff903308b63))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.17 ([#852](https://github.com/k8sgpt-ai/k8sgpt/issues/852)) ([85ebd12](https://github.com/k8sgpt-ai/k8sgpt/commit/85ebd12c30d369c5ef9a42b5a834d091523a7b6e))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.18 ([#856](https://github.com/k8sgpt-ai/k8sgpt/issues/856)) ([4106d39](https://github.com/k8sgpt-ai/k8sgpt/commit/4106d39c322940413ebfd9ac0bf6f5bd31830e93))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.19 ([#859](https://github.com/k8sgpt-ai/k8sgpt/issues/859)) ([6a2f315](https://github.com/k8sgpt-ai/k8sgpt/commit/6a2f315b2f4344f2924b7915e8a1393f9732a1e9))
* **deps:** update module github.com/sashabaranov/go-openai to v1.17.11 ([#853](https://github.com/k8sgpt-ai/k8sgpt/issues/853)) ([1979c86](https://github.com/k8sgpt-ai/k8sgpt/commit/1979c86d0f59921d55cd4229a37d604a6f1dc578))
* **deps:** update module github.com/sashabaranov/go-openai to v1.17.11 ([#861](https://github.com/k8sgpt-ai/k8sgpt/issues/861)) ([40b5b7e](https://github.com/k8sgpt-ai/k8sgpt/commit/40b5b7e185c8d335bdefb131988b9900ad26bac3))
* **deps:** update module gopkg.in/yaml.v2 to v3 ([#864](https://github.com/k8sgpt-ai/k8sgpt/issues/864)) ([36ba6c5](https://github.com/k8sgpt-ai/k8sgpt/commit/36ba6c5147a9ed75c14dbba4bc06cae903e651a4))
* **deps:** update module gopkg.in/yaml.v2 to v3 ([#865](https://github.com/k8sgpt-ai/k8sgpt/issues/865)) ([c55025d](https://github.com/k8sgpt-ai/k8sgpt/commit/c55025d04ebf9da0f6092aabb0b043ccef05164c))
### Other
* **deps:** update actions/upload-artifact digest to 1eb3cb2 ([#867](https://github.com/k8sgpt-ai/k8sgpt/issues/867)) ([4ce56f3](https://github.com/k8sgpt-ai/k8sgpt/commit/4ce56f38b4338a6a2fe69f588b0f17e0b54d0ae6))
* **deps:** update anchore/sbom-action action to v0.15.3 ([#850](https://github.com/k8sgpt-ai/k8sgpt/issues/850)) ([12f764d](https://github.com/k8sgpt-ai/k8sgpt/commit/12f764d5846accbd987d40f69a153dceb9954f39))
### Docs
* adjusted README information about providers ([#844](https://github.com/k8sgpt-ai/k8sgpt/issues/844)) ([745e960](https://github.com/k8sgpt-ai/k8sgpt/commit/745e960f492e6dd0e50aa4a1ce7239c677025024))
## [0.3.25](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.24...v0.3.25) (2024-01-05)
### Features
* added Google GenAI client; simplified IAI/clients API surface. ([#829](https://github.com/k8sgpt-ai/k8sgpt/issues/829)) ([e7d4149](https://github.com/k8sgpt-ai/k8sgpt/commit/e7d41496ddaa145c70079852da8b2ce3b3b7289f))
* code_cov badge ([#821](https://github.com/k8sgpt-ai/k8sgpt/issues/821)) ([fcd29a5](https://github.com/k8sgpt-ai/k8sgpt/commit/fcd29a547d73ba48935762e2f568f5755f5c6ed3))
* coverage reports ([#819](https://github.com/k8sgpt-ai/k8sgpt/issues/819)) ([3d0ba3e](https://github.com/k8sgpt-ai/k8sgpt/commit/3d0ba3e78cabaf5f1262c5b5b16ebabad974fa87))
### Bug Fixes
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.10 ([#811](https://github.com/k8sgpt-ai/k8sgpt/issues/811)) ([e5cc4a2](https://github.com/k8sgpt-ai/k8sgpt/commit/e5cc4a28cb3682e7094e6ceddf91b65da991ddb6))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.12 ([#813](https://github.com/k8sgpt-ai/k8sgpt/issues/813)) ([91613ba](https://github.com/k8sgpt-ai/k8sgpt/commit/91613baa5cc5244c93deb344abcdd905802eef30))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.14 ([#822](https://github.com/k8sgpt-ai/k8sgpt/issues/822)) ([526e22f](https://github.com/k8sgpt-ai/k8sgpt/commit/526e22f88b8de15eceb10965b045ef0366ff2d6c))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.15 ([#835](https://github.com/k8sgpt-ai/k8sgpt/issues/835)) ([e78ff05](https://github.com/k8sgpt-ai/k8sgpt/commit/e78ff054190cd54cabe17d77ac69443e517f1e55))
* **deps:** update module github.com/prometheus/client_golang to v1.18.0 ([#814](https://github.com/k8sgpt-ai/k8sgpt/issues/814)) ([6eb8f67](https://github.com/k8sgpt-ai/k8sgpt/commit/6eb8f6793ed989ba3ac7ed00336345f68b09bf45))
* **deps:** update module github.com/sashabaranov/go-openai to v1.17.10 ([#824](https://github.com/k8sgpt-ai/k8sgpt/issues/824)) ([4314804](https://github.com/k8sgpt-ai/k8sgpt/commit/4314804ca7e782f5149dc2078ba9c859edc4688a))
* **deps:** update module golang.org/x/term to v0.16.0 ([#831](https://github.com/k8sgpt-ai/k8sgpt/issues/831)) ([4de989c](https://github.com/k8sgpt-ai/k8sgpt/commit/4de989c803ee43a02d75112d1b3a54daee3dd9af))
* **deps:** update module google.golang.org/api to v0.155.0 ([#836](https://github.com/k8sgpt-ai/k8sgpt/issues/836)) ([105a239](https://github.com/k8sgpt-ai/k8sgpt/commit/105a239d94384f4096c01d9978564040773ab56e))
* no explain case, improved readability. ([#825](https://github.com/k8sgpt-ai/k8sgpt/issues/825)) ([035348d](https://github.com/k8sgpt-ai/k8sgpt/commit/035348d8a0d290ac26b42425945eaafe038cedc5))
### Other
* added basic server startup test ([#817](https://github.com/k8sgpt-ai/k8sgpt/issues/817)) ([3e7cea7](https://github.com/k8sgpt-ai/k8sgpt/commit/3e7cea7bd39253718bc3d2f8b10ac5fc9b98cbc2))
* **deps:** pin codecov/codecov-action action to eaaf4be ([#820](https://github.com/k8sgpt-ai/k8sgpt/issues/820)) ([2f0f2df](https://github.com/k8sgpt-ai/k8sgpt/commit/2f0f2dfa8a5957cb8b10864c14d7883158723a6a))
* **deps:** update anchore/sbom-action action to v0.15.2 ([#823](https://github.com/k8sgpt-ai/k8sgpt/issues/823)) ([70c6892](https://github.com/k8sgpt-ai/k8sgpt/commit/70c68929d8d963c0bd17390c76e366d4339f56b9))
* lint fixes ([#833](https://github.com/k8sgpt-ai/k8sgpt/issues/833)) ([a7e9b48](https://github.com/k8sgpt-ai/k8sgpt/commit/a7e9b486bad7c2d62878e470a755d1fef3803680))
* remove code cov ([#832](https://github.com/k8sgpt-ai/k8sgpt/issues/832)) ([a774265](https://github.com/k8sgpt-ai/k8sgpt/commit/a77426593d7f3a8cfa810336ff08a2266db7fb4f))
### Dependency Updates
* go module bump to fix CVE: GHSA-45x7-px36-x8w8 & GHSA-7ww5-4wqc-m92c ([#810](https://github.com/k8sgpt-ai/k8sgpt/issues/810)) ([b17fd7c](https://github.com/k8sgpt-ai/k8sgpt/commit/b17fd7c98644afa70d414fcb32e49e61e1c831ad))
## [0.3.24](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.23...v0.3.24) (2023-12-23)
### Features
* add last termination state when pod is in CrashloopBackoff ([#792](https://github.com/k8sgpt-ai/k8sgpt/issues/792)) ([ff4aaf7](https://github.com/k8sgpt-ai/k8sgpt/commit/ff4aaf7c328a58fcad8e4fb0f93ea543725eedd5))
* Add license scan report and status ([#796](https://github.com/k8sgpt-ai/k8sgpt/issues/796)) ([343aec8](https://github.com/k8sgpt-ai/k8sgpt/commit/343aec8f0455c9461eb8d495ca5bd446b4bad667))
* version upgrade to 1.21 ([#798](https://github.com/k8sgpt-ai/k8sgpt/issues/798)) ([c23f24d](https://github.com/k8sgpt-ai/k8sgpt/commit/c23f24de2e79347e4f5465e28af34e138cc13231))
### Bug Fixes
* added the ability to set the trivy variables by the user ([#797](https://github.com/k8sgpt-ai/k8sgpt/issues/797)) ([928b39a](https://github.com/k8sgpt-ai/k8sgpt/commit/928b39a7283ee274dd517e727624eceb3795594d))
* **deps:** update module cloud.google.com/go/storage to v1.36.0 ([#805](https://github.com/k8sgpt-ai/k8sgpt/issues/805)) ([390f309](https://github.com/k8sgpt-ai/k8sgpt/commit/390f30908800dfe21e2c1660139b0bd9d36b34d6))
* **deps:** update module github.com/aquasecurity/trivy-operator to v0.17.1 ([#780](https://github.com/k8sgpt-ai/k8sgpt/issues/780)) ([71f36bd](https://github.com/k8sgpt-ai/k8sgpt/commit/71f36bdb0b3729c4357299b7d03829dd5b6a69ec))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.6 ([#783](https://github.com/k8sgpt-ai/k8sgpt/issues/783)) ([1b386f6](https://github.com/k8sgpt-ai/k8sgpt/commit/1b386f64f2863d8a49f423ad571cba009807bc55))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.7 ([#804](https://github.com/k8sgpt-ai/k8sgpt/issues/804)) ([3c6c759](https://github.com/k8sgpt-ai/k8sgpt/commit/3c6c7597e014bfd68794b1764c3a8902e8a798ea))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.8 ([#807](https://github.com/k8sgpt-ai/k8sgpt/issues/807)) ([93b5ca1](https://github.com/k8sgpt-ai/k8sgpt/commit/93b5ca1985c3730592388ba6fc32ecca9b806888))
* **deps:** update module github.com/aws/aws-sdk-go to v1.49.9 ([#808](https://github.com/k8sgpt-ai/k8sgpt/issues/808)) ([130e4c2](https://github.com/k8sgpt-ai/k8sgpt/commit/130e4c2efd0e5b34cdc84c357c6c1f3987cf7c35))
* **deps:** update module github.com/azure/azure-sdk-for-go/sdk/storage/azblob to v1.2.1 ([#801](https://github.com/k8sgpt-ai/k8sgpt/issues/801)) ([aa05756](https://github.com/k8sgpt-ai/k8sgpt/commit/aa057565b5c971c493443f3ede4aed8f8a6399f7))
* **deps:** update module github.com/mittwald/go-helm-client to v0.12.5 ([#802](https://github.com/k8sgpt-ai/k8sgpt/issues/802)) ([4a7bad3](https://github.com/k8sgpt-ai/k8sgpt/commit/4a7bad313b66750bd830413b7fef005580ad843c))
* **deps:** update module github.com/sashabaranov/go-openai to v1.17.9 ([#772](https://github.com/k8sgpt-ai/k8sgpt/issues/772)) ([13d64a5](https://github.com/k8sgpt-ai/k8sgpt/commit/13d64a58750c7262c07042b557fbf2c4a511b777))
* **deps:** update module github.com/spf13/viper to v1.18.2 ([#787](https://github.com/k8sgpt-ai/k8sgpt/issues/787)) ([8dea617](https://github.com/k8sgpt-ai/k8sgpt/commit/8dea6170a2c00c03f08f25e4f0a232be617536f1))
* **deps:** update module google.golang.org/api to v0.154.0 ([#779](https://github.com/k8sgpt-ai/k8sgpt/issues/779)) ([78f7f2b](https://github.com/k8sgpt-ai/k8sgpt/commit/78f7f2ba85fd357cab13ccc15e9e767e8611773a))
* **deps:** update module google.golang.org/grpc to v1.60.1 ([#790](https://github.com/k8sgpt-ai/k8sgpt/issues/790)) ([5d54c3f](https://github.com/k8sgpt-ai/k8sgpt/commit/5d54c3f840a9ce002606b6601187e69fb62f8a28))
* **deps:** update module helm.sh/helm/v3 to v3.13.3 ([#803](https://github.com/k8sgpt-ai/k8sgpt/issues/803)) ([a8e1932](https://github.com/k8sgpt-ai/k8sgpt/commit/a8e193212222811f3a278df6056dd2165c4323bd))
* lowercase logs before running regex matching in LogAnalyzer ([#794](https://github.com/k8sgpt-ai/k8sgpt/issues/794)) ([03b63be](https://github.com/k8sgpt-ai/k8sgpt/commit/03b63befa247ac84b795a0ec8d5280196b8d570d))
### Other
* **deps:** update actions/setup-go action to v5 ([#788](https://github.com/k8sgpt-ai/k8sgpt/issues/788)) ([d00ed33](https://github.com/k8sgpt-ai/k8sgpt/commit/d00ed33678b1560a3996f1d735d84ca0ca05c0b0))
* **deps:** update actions/upload-artifact action to v4 ([#806](https://github.com/k8sgpt-ai/k8sgpt/issues/806)) ([d6fb648](https://github.com/k8sgpt-ai/k8sgpt/commit/d6fb648e23c1ed1e4680fc4b7b4e96501f50ad48))
* **deps:** update anchore/sbom-action action to v0.15.1 ([#784](https://github.com/k8sgpt-ai/k8sgpt/issues/784)) ([6473a2b](https://github.com/k8sgpt-ai/k8sgpt/commit/6473a2b532491b707b3af922fc2198e626ebf219))
* **deps:** update google-github-actions/release-please-action action to v4 ([#782](https://github.com/k8sgpt-ai/k8sgpt/issues/782)) ([2c28c55](https://github.com/k8sgpt-ai/k8sgpt/commit/2c28c555cf4e891b90ebd9e9eae1cd8724e9886f))
* **deps:** update google-github-actions/release-please-action action to v4.0.2 ([#800](https://github.com/k8sgpt-ai/k8sgpt/issues/800)) ([be4b0bb](https://github.com/k8sgpt-ai/k8sgpt/commit/be4b0bb3c24e04d35f40d16fd8e94ddbc8457ca6))
### Refactoring
* replace rest client with controller-runtime clientset for Trivy analyzers ([#776](https://github.com/k8sgpt-ai/k8sgpt/issues/776)) ([1d19628](https://github.com/k8sgpt-ai/k8sgpt/commit/1d196286b75f0ea6c068e8bdb01455fb36c52432))
## [0.3.23](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.22...v0.3.23) (2023-11-24)
### Features
* add Gateway analysers ([#764](https://github.com/k8sgpt-ai/k8sgpt/issues/764)) ([ec08cac](https://github.com/k8sgpt-ai/k8sgpt/commit/ec08cac21496b34b123b75b06d9283eb6539e890))
### Bug Fixes
* **deps:** update module github.com/aws/aws-sdk-go to v1.48.3 ([#768](https://github.com/k8sgpt-ai/k8sgpt/issues/768)) ([b1c791a](https://github.com/k8sgpt-ai/k8sgpt/commit/b1c791a396b7287ef916e8f8d382a0e14ba39949))
* **deps:** update module github.com/mittwald/go-helm-client to v0.12.4 ([#767](https://github.com/k8sgpt-ai/k8sgpt/issues/767)) ([dca5b47](https://github.com/k8sgpt-ai/k8sgpt/commit/dca5b4710d1bb35dfc3346219d3bddb7c726300e))
## [0.3.22](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.21...v0.3.22) (2023-11-21)
### Features
* rework cache package - add gcs cache - add cache purge command ([#750](https://github.com/k8sgpt-ai/k8sgpt/issues/750)) ([12146bf](https://github.com/k8sgpt-ai/k8sgpt/commit/12146bf356a3b26176c47e3a013a713fd14f346d))
### Bug Fixes
* cover more error reason messages ([#759](https://github.com/k8sgpt-ai/k8sgpt/issues/759)) ([5b27c3e](https://github.com/k8sgpt-ai/k8sgpt/commit/5b27c3e352701819f1d0449df9acf706040f1f13))
* **deps:** update kubernetes packages to v0.28.4 ([#756](https://github.com/k8sgpt-ai/k8sgpt/issues/756)) ([24132c2](https://github.com/k8sgpt-ai/k8sgpt/commit/24132c2d87024157009589cf2bd410bac2a26241))
* **deps:** update module cloud.google.com/go/storage to v1.35.1 ([#762](https://github.com/k8sgpt-ai/k8sgpt/issues/762)) ([58d182e](https://github.com/k8sgpt-ai/k8sgpt/commit/58d182e94f75f9b035a9e45159fa87ce8a57de38))
* **deps:** update module github.com/aquasecurity/trivy-operator to v0.16.4 ([#676](https://github.com/k8sgpt-ai/k8sgpt/issues/676)) ([4531278](https://github.com/k8sgpt-ai/k8sgpt/commit/45312788c3c15e141027c3fc8e428cfaa71d3ace))
* **deps:** update module github.com/aws/aws-sdk-go to v1.47.10 ([#751](https://github.com/k8sgpt-ai/k8sgpt/issues/751)) ([2aa31bc](https://github.com/k8sgpt-ai/k8sgpt/commit/2aa31bc66d239906b1047f53bcaa58b0c30a2856))
* **deps:** update module github.com/aws/aws-sdk-go to v1.47.11 ([#752](https://github.com/k8sgpt-ai/k8sgpt/issues/752)) ([531fa79](https://github.com/k8sgpt-ai/k8sgpt/commit/531fa79ed640846b177c516559dc82f088fa940f))
* **deps:** update module github.com/aws/aws-sdk-go to v1.48.0 ([#754](https://github.com/k8sgpt-ai/k8sgpt/issues/754)) ([e2bb567](https://github.com/k8sgpt-ai/k8sgpt/commit/e2bb567d2f8d59a904583309c2774d4174eb367f))
* **deps:** update module github.com/aws/aws-sdk-go to v1.48.1 ([#766](https://github.com/k8sgpt-ai/k8sgpt/issues/766)) ([16469c0](https://github.com/k8sgpt-ai/k8sgpt/commit/16469c01c962fd5bfa4ad11dd88a41f3e00e4a0d))
* **deps:** update module github.com/sashabaranov/go-openai to v1.17.6 ([#749](https://github.com/k8sgpt-ai/k8sgpt/issues/749)) ([84df364](https://github.com/k8sgpt-ai/k8sgpt/commit/84df3640bc114bb2c768f158d3575732103ff799))
* **deps:** update module github.com/sashabaranov/go-openai to v1.17.7 ([#753](https://github.com/k8sgpt-ai/k8sgpt/issues/753)) ([9971699](https://github.com/k8sgpt-ai/k8sgpt/commit/9971699fcf42b3309449d81875d45180f723de8d))
* **deps:** update module github.com/sashabaranov/go-openai to v1.17.8 ([#761](https://github.com/k8sgpt-ai/k8sgpt/issues/761)) ([beaa532](https://github.com/k8sgpt-ai/k8sgpt/commit/beaa53251c8201028db83d60f208e2b0658c93d8))
* **deps:** update module google.golang.org/api to v0.151.0 ([#763](https://github.com/k8sgpt-ai/k8sgpt/issues/763)) ([3e3f6a9](https://github.com/k8sgpt-ai/k8sgpt/commit/3e3f6a903a81d9622660f5adf9cae7d22a5c99f4))
* show trivy as active when activated with --no-install flag ([#675](https://github.com/k8sgpt-ai/k8sgpt/issues/675)) ([7368271](https://github.com/k8sgpt-ai/k8sgpt/commit/73682717eda4fa2e0cbc6311d5c97e01e0f2673c))
### Other
* **deps:** update anchore/sbom-action action to v0.15.0 ([#765](https://github.com/k8sgpt-ai/k8sgpt/issues/765)) ([cf1e243](https://github.com/k8sgpt-ai/k8sgpt/commit/cf1e243708ab406f070da3f96be1fc60b7ce2ea4))
* **deps:** update docker/build-push-action digest to 4a13e50 ([#760](https://github.com/k8sgpt-ai/k8sgpt/issues/760)) ([b5853de](https://github.com/k8sgpt-ai/k8sgpt/commit/b5853de8a6fcd17b1c1a4c53dbe3ffc82b83f72f))
## [0.3.21](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.20...v0.3.21) (2023-11-12)
### Features
* auth remove: add -b flag ([#711](https://github.com/k8sgpt-ai/k8sgpt/issues/711)) ([9dadd18](https://github.com/k8sgpt-ai/k8sgpt/commit/9dadd186c8d03a4284faff3f0842d6e2d00ebbb8))
* log analyzer ([#744](https://github.com/k8sgpt-ai/k8sgpt/issues/744)) ([d365886](https://github.com/k8sgpt-ai/k8sgpt/commit/d365886753f785bd58118c03510696318ea47941))
### Bug Fixes
* **deps:** update module github.com/aws/aws-sdk-go to v1.47.6 ([#728](https://github.com/k8sgpt-ai/k8sgpt/issues/728)) ([bb21ce8](https://github.com/k8sgpt-ai/k8sgpt/commit/bb21ce80c782e011dfa1f808ccdd82ae748bfed8))
* **deps:** update module github.com/aws/aws-sdk-go to v1.47.8 ([#741](https://github.com/k8sgpt-ai/k8sgpt/issues/741)) ([d359caa](https://github.com/k8sgpt-ai/k8sgpt/commit/d359caaab6bdb42a54d305be2f4cd8452f512bb8))
* **deps:** update module github.com/aws/aws-sdk-go to v1.47.9 ([#743](https://github.com/k8sgpt-ai/k8sgpt/issues/743)) ([45ebad7](https://github.com/k8sgpt-ai/k8sgpt/commit/45ebad7b4d80d93920d5fbad9f42c8fcd45218bd))
* **deps:** update module github.com/fatih/color to v1.16.0 ([#734](https://github.com/k8sgpt-ai/k8sgpt/issues/734)) ([8ab26d9](https://github.com/k8sgpt-ai/k8sgpt/commit/8ab26d96cec73369ecf014d50fccc26afe15fa44))
* **deps:** update module github.com/sashabaranov/go-openai to v1.17.3 ([#737](https://github.com/k8sgpt-ai/k8sgpt/issues/737)) ([48486e9](https://github.com/k8sgpt-ai/k8sgpt/commit/48486e96274a5e52a03cef00bd531148e27b38c5))
* **deps:** update module github.com/sashabaranov/go-openai to v1.17.5 ([#742](https://github.com/k8sgpt-ai/k8sgpt/issues/742)) ([3bff9cb](https://github.com/k8sgpt-ai/k8sgpt/commit/3bff9cbe7bb3afb7212735eb91902fd83d3cbb8c))
* **deps:** update module github.com/schollz/progressbar/v3 to v3.14.1 ([#738](https://github.com/k8sgpt-ai/k8sgpt/issues/738)) ([05f444d](https://github.com/k8sgpt-ai/k8sgpt/commit/05f444dec1f234c191e25f71f3eab4838eb2477a))
* **deps:** update module github.com/spf13/cobra to v1.8.0 ([#732](https://github.com/k8sgpt-ai/k8sgpt/issues/732)) ([19e502a](https://github.com/k8sgpt-ai/k8sgpt/commit/19e502a841e0463b682b0c6b8291f10aee616d7e))
* **deps:** update module helm.sh/helm/v3 to v3.13.2 ([#740](https://github.com/k8sgpt-ai/k8sgpt/issues/740)) ([6a665f0](https://github.com/k8sgpt-ai/k8sgpt/commit/6a665f05d782ba9c3051df7a15ff304c89cb34f4))
### Other
* **deps:** pin google-github-actions/release-please-action action to db8f2c6 ([#747](https://github.com/k8sgpt-ai/k8sgpt/issues/747)) ([4408110](https://github.com/k8sgpt-ai/k8sgpt/commit/4408110b1a4835bb237b3d5674d6fa8a13f0181b))
* **deps:** update google-github-actions/release-please-action digest to 4c5670f ([#721](https://github.com/k8sgpt-ai/k8sgpt/issues/721)) ([9c518ba](https://github.com/k8sgpt-ai/k8sgpt/commit/9c518badf53e4ccd9c2f9251cead4692602c0762))
* **deps:** update google-github-actions/release-please-action digest to db8f2c6 ([#736](https://github.com/k8sgpt-ai/k8sgpt/issues/736)) ([fdb2934](https://github.com/k8sgpt-ai/k8sgpt/commit/fdb2934e8fd02bcb4e47b34c1eca5b099f462faa))
* enable automerge for renovate ([#745](https://github.com/k8sgpt-ai/k8sgpt/issues/745)) ([66ebb88](https://github.com/k8sgpt-ai/k8sgpt/commit/66ebb88efe1ad5ecae75a5299f58a1e68179b515))
* pin release-please version ([#746](https://github.com/k8sgpt-ai/k8sgpt/issues/746)) ([c4925b2](https://github.com/k8sgpt-ai/k8sgpt/commit/c4925b2170546d0d86b77d2a13c13d4907e2e3d6))
### Dependency Updates
* bump docker fixes CVE GHSA-jq35-85cj-fj4p ([#733](https://github.com/k8sgpt-ai/k8sgpt/issues/733)) ([120027e](https://github.com/k8sgpt-ai/k8sgpt/commit/120027e3cbec2535f0b6cc8d8db1dc27dd9f3ec6))
## [0.3.20](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.19...v0.3.20) (2023-11-05)
### Features
* amazonsagemaker AI provider ([#731](https://github.com/k8sgpt-ai/k8sgpt/issues/731)) ([ccef7f6](https://github.com/k8sgpt-ai/k8sgpt/commit/ccef7f617004723b37d1e8ffb011398005e0b392))
### Bug Fixes
* **deps:** update module github.com/aws/aws-sdk-go to v1.47.1 ([#724](https://github.com/k8sgpt-ai/k8sgpt/issues/724)) ([0136b8f](https://github.com/k8sgpt-ai/k8sgpt/commit/0136b8f543a7052e967e29691afe1aab8e5fae1b))
* **deps:** update module github.com/azure/azure-sdk-for-go/sdk/storage/azblob to v1.2.0 ([#723](https://github.com/k8sgpt-ai/k8sgpt/issues/723)) ([16b229d](https://github.com/k8sgpt-ai/k8sgpt/commit/16b229d5478085655041ff0230d2542c4c0c7ce9))
* **deps:** update module google.golang.org/grpc to v1.59.0 ([#713](https://github.com/k8sgpt-ai/k8sgpt/issues/713)) ([901c5ec](https://github.com/k8sgpt-ai/k8sgpt/commit/901c5ec18858f2f7fd385ff20aef77d203748c93))
* **deps:** update module helm.sh/helm/v3 to v3.13.1 ([#706](https://github.com/k8sgpt-ai/k8sgpt/issues/706)) ([40133ad](https://github.com/k8sgpt-ai/k8sgpt/commit/40133adaedff3862199e00f62877a88fcffa67c5))
* ensure ingress HTTP rule exists to prevent panic ([#726](https://github.com/k8sgpt-ai/k8sgpt/issues/726)) ([37721b5](https://github.com/k8sgpt-ai/k8sgpt/commit/37721b5dd77d66edfb7e8377b2b96470b8a21d1b))
### Other
* **deps:** update amannn/action-semantic-pull-request action to v5.4.0 ([#729](https://github.com/k8sgpt-ai/k8sgpt/issues/729)) ([188a8a2](https://github.com/k8sgpt-ai/k8sgpt/commit/188a8a2cd5e25b35446e2eab46279a0ba3976af3))
## [0.3.19](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.3.18...v0.3.19) (2023-10-28)

190
README.md
View File

@@ -9,6 +9,8 @@
![GitHub release (latest by date)](https://img.shields.io/github/v/release/k8sgpt-ai/k8sgpt)
[![OpenSSF Best Practices](https://bestpractices.coreinfrastructure.org/projects/7272/badge)](https://bestpractices.coreinfrastructure.org/projects/7272)
[![Link to documentation](https://img.shields.io/static/v1?label=%F0%9F%93%96&message=Documentation&color=blue)](https://docs.k8sgpt.ai/)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fk8sgpt-ai%2Fk8sgpt.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fk8sgpt-ai%2Fk8sgpt?ref=badge_shield)
[![codecov](https://codecov.io/github/k8sgpt-ai/k8sgpt/graph/badge.svg?token=ZLR7NG8URE)](https://codecov.io/github/k8sgpt-ai/k8sgpt)
`k8sgpt` is a tool for scanning your Kubernetes clusters, diagnosing, and triaging issues in simple English.
@@ -36,7 +38,7 @@ brew install k8sgpt
**32 bit:**
<!---x-release-please-start-version-->
```
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.19/k8sgpt_386.rpm
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.26/k8sgpt_386.rpm
sudo rpm -ivh k8sgpt_386.rpm
```
<!---x-release-please-end-->
@@ -45,7 +47,7 @@ brew install k8sgpt
<!---x-release-please-start-version-->
```
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.19/k8sgpt_amd64.rpm
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.26/k8sgpt_amd64.rpm
sudo rpm -ivh -i k8sgpt_amd64.rpm
```
<!---x-release-please-end-->
@@ -57,7 +59,7 @@ brew install k8sgpt
**32 bit:**
<!---x-release-please-start-version-->
```
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.19/k8sgpt_386.deb
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.26/k8sgpt_386.deb
sudo dpkg -i k8sgpt_386.deb
```
<!---x-release-please-end-->
@@ -65,7 +67,7 @@ brew install k8sgpt
<!---x-release-please-start-version-->
```
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.19/k8sgpt_amd64.deb
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.26/k8sgpt_amd64.deb
sudo dpkg -i k8sgpt_amd64.deb
```
<!---x-release-please-end-->
@@ -78,14 +80,14 @@ brew install k8sgpt
**32 bit:**
<!---x-release-please-start-version-->
```
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.19/k8sgpt_386.apk
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.26/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.19/k8sgpt_amd64.apk
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.3.26/k8sgpt_amd64.apk
apk add k8sgpt_amd64.apk
```
<!---x-release-please-end-->x
@@ -123,14 +125,14 @@ _This mode of operation is ideal for continuous monitoring of your cluster and c
## Quick Start
* Currently the default AI provider is OpenAI, you will need to generate an API key from [OpenAI](https://openai.com)
* Currently, the default AI provider is OpenAI, you will need to generate an API key from [OpenAI](https://openai.com)
* You can do this by running `k8sgpt generate` to open a browser link to generate it
* Run `k8sgpt auth add` to set it in k8sgpt.
* You can provide the password directly using the `--password` flag.
* Run `k8sgpt filters` to manage the active filters used by the analyzer. By default, all filters are executed during analysis.
* Run `k8sgpt analyze` to run a scan.
* And use `k8sgpt analyze --explain` to get a more detailed explanation of the issues.
* You also run `k8sgpt analyze --with-doc` (with or without the explain flag) to get the official documentation from kubernetes.
* You also run `k8sgpt analyze --with-doc` (with or without the explain flag) to get the official documentation from Kubernetes.
## Analyzers
@@ -159,6 +161,9 @@ you will be able to write your own analyzers.
- [x] hpaAnalyzer
- [x] pdbAnalyzer
- [x] networkPolicyAnalyzer
- [x] gatewayClass
- [x] gateway
- [x] httproute
## Examples
@@ -246,7 +251,7 @@ k8sgpt auth update $MY_BACKEND1,$MY_BACKEND2..
_Remove configured backends_
```
k8sgpt auth remove $MY_BACKEND1,$MY_BACKEND2..
k8sgpt auth remove -b $MY_BACKEND1,$MY_BACKEND2..
```
_List integrations_
@@ -282,146 +287,32 @@ k8sgpt serve
_Analysis with serve mode_
```
curl -X GET "http://localhost:8080/analyze?namespace=k8sgpt&explain=false"
grpcurl -plaintext -d '{"namespace": "k8sgpt", "explain": false}' localhost:8080 schema.v1.ServerService/Analyze
```
</details>
## LLM AI Backends
## Key Features
K8sGPT uses the chosen LLM, generative AI provider when you want to explain the analysis results using --explain flag e.g. `k8sgpt analyze --explain`. You can use `--backend` flag to specify a configured provider (it's `openai` by default).
<details>
<summary> LocalAI provider </summary>
To run local models, it is possible to use OpenAI compatible APIs, for instance [LocalAI](https://github.com/go-skynet/LocalAI) which uses [llama.cpp](https://github.com/ggerganov/llama.cpp) and [ggml](https://github.com/ggerganov/ggml) to run inference on consumer-grade hardware. Models supported by LocalAI for instance are Vicuna, Alpaca, LLaMA, Cerebras, GPT4ALL, GPT4ALL-J and koala.
To run local inference, you need to download the models first, for instance you can find `ggml` compatible models in [huggingface.com](https://huggingface.co/models?search=ggml) (for example vicuna, alpaca and koala).
### Start the API server
To start the API server, follow the instruction in [LocalAI](https://github.com/go-skynet/LocalAI#example-use-gpt4all-j-model).
### Run k8sgpt
To run k8sgpt, run `k8sgpt auth add` with the `localai` backend:
You can list available providers using `k8sgpt auth list`:
```
k8sgpt auth add --backend localai --model <model_name> --baseurl http://localhost:8080/v1 --temperature 0.7
```
Now you can analyze with the `localai` backend:
```
k8sgpt analyze --explain --backend localai
```
</details>
<details>
<summary> AzureOpenAI provider </summary>
<em>Prerequisites:</em> an Azure OpenAI deployment is needed, please visit MS official [documentation](https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource) to create your own.
To authenticate with k8sgpt, you will need the Azure OpenAI endpoint of your tenant `"https://your Azure OpenAI Endpoint"`, the api key to access your deployment, the deployment name of your model and the model name itself.
To run k8sgpt, run `k8sgpt auth` with the `azureopenai` backend:
```
k8sgpt auth add --backend azureopenai --baseurl https://<your Azure OpenAI endpoint> --engine <deployment_name> --model <model_name>
```
Lastly, enter your Azure API key, after the prompt.
Now you are ready to analyze with the azure openai backend:
```
k8sgpt analyze --explain --backend azureopenai
```
</details>
<details>
<summary>Cohere provider</summary>
<em>Prerequisites:</em> a Cohere API key is needed, please visit the [Cohere dashboard](https://dashboard.cohere.ai/api-keys) to create one.
To run k8sgpt, run `k8sgpt auth` with the `cohere` backend:
```
k8sgpt auth add --backend cohere --model command-nightly
```
Lastly, enter your Cohere API key, after the prompt.
Now you are ready to analyze with the Cohere backend:
```
k8sgpt analyze --explain --backend cohere
```
</details>
<details>
<summary>Amazon Bedrock provider</summary>
<em>Prerequisites</em>
Bedrock API access is needed.
<img src="images/bedrock.png" width="500px;" />
As illustrated below, you will need to enable this in the [AWS Console](https://eu-central-1.console.aws.amazon.com/bedrock/home?region=eu-central-1#/modelaccess)
In addition to this you will need to set the follow local environmental variables:
```
- AWS_ACCESS_KEY
- AWS_SECRET_ACCESS_KEY
- AWS_DEFAULT_REGION
```
```
k8sgpt auth add --backend amazonbedrock --model anthropic.claude-v2
```
TODO: Currently access key will be requested in the CLI, you can enter anything into this.
#### Usage
```
k8sgpt analyze -e -b amazonbedrock
0 argocd/argocd-application-controller(argocd-application-controller)
- Error: StatefulSet uses the service argocd/argocd-application-controller which does not exist.
You're right, I don't have enough context to determine if a StatefulSet is correctly configured to use a non-existent service. A StatefulSet manages Pods with persistent storage, and the Pods are created from the same spec. The service name referenced in the StatefulSet configuration would need to match an existing Kubernetes service for the Pods to connect to. Without more details on the specific StatefulSet and environment, I can't confirm whether the configuration is valid or not.
```
</details>
<details>
<summary>Setting a new default AI provider</summary>
There may be scenarios where you wish to have K8sGPT plugged into several default AI providers. In this case you may wish to use one as a new default, other than OpenAI which is the project default.
_To view available providers_
```
k8sgpt auth list
Default:
Default:
> openai
Active:
Active:
Unused:
> openai
> azureopenai
Unused:
> localai
> noopai
> amazonbedrock
> azureopenai
> cohere
> amazonbedrock
> amazonsagemaker
> google
> noopai
```
For detailed documentation on how to configure and use each provider see [here](https://docs.k8sgpt.ai/reference/providers/backend/).
_To set a new default provider_
@@ -430,15 +321,12 @@ k8sgpt auth default -p azureopenai
Default provider set to azureopenai
```
</details>
## Key Features
<details>
With this option, the data is anonymized before being sent to the AI Backend. During the analysis execution, `k8sgpt` retrieves sensitive data (Kubernetes object names, labels, etc.). This data is masked when sent to the AI backend and replaced by a key that can be used to de-anonymize the data when the solution is returned to the user.
<summary> Anonymization </summary>
1. Error reported during analysis:
@@ -537,19 +425,29 @@ _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 --region <aws region> --bucket <name> ```
* Configuration, ``` k8sgpt cache add s3 --region <aws region> --bucket <name> ```
* K8sGPT will create the bucket if it does not exist
* Azure Storage
* We support a number of [techniques](https://learn.microsoft.com/en-us/azure/developer/go/azure-sdk-authentication?tabs=bash#2-authenticate-with-azure) to authenticate against Azure
* Configuration, ``` k8sgpt cache add --storageacc <storage account name> --container <container name> ```
* Configuration, ``` k8sgpt cache add azure --storageacc <storage account name> --container <container name> ```
* K8sGPT assumes that the storage account already exist and it will create the container if it does not exist
* It's **users'** responsibility have to grant specific permissions to their identity in order to be able to upload blob files and create SA containers (e.g Storage Blob Data Contributor)
* It is the **user** responsibility have to grant specific permissions to their identity in order to be able to upload blob files and create SA containers (e.g Storage Blob Data Contributor)
* Google Cloud Storage
* _As a prerequisite `GOOGLE_APPLICATION_CREDENTIALS` are required as environmental variables._
* Configuration, ``` k8sgpt cache add gcs --region <gcp region> --bucket <name> --projectid <project id>```
* K8sGPT will create the bucket if it does not exist
_Listing cache items_
```
k8sgpt cache list
```
_Purging an object from the cache_
Note: purging an object using this command will delete upstream files, so it requires appropriate permissions.
```
k8sgpt cache purge $OBJECT_NAME
```
_Removing the remote cache_
Note: this will not delete the upstream S3 bucket or Azure storage container
```
@@ -567,8 +465,12 @@ Find our official documentation available [here](https://docs.k8sgpt.ai)
Please read our [contributing guide](./CONTRIBUTING.md).
## Community
Find us on [Slack](https://join.slack.com/t/k8sgpt/shared_invite/zt-1rwe5fpzq-VNtJK8DmYbbm~iWL1H34nw)
Find us on [Slack](https://join.slack.com/t/k8sgpt/shared_invite/zt-276pa9uyq-pxAUr4TCVHubFxEvLZuT1Q)
<a href="https://github.com/k8sgpt-ai/k8sgpt/graphs/contributors">
<img src="https://contrib.rocks/image?repo=k8sgpt-ai/k8sgpt" />
</a>
## License
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fk8sgpt-ai%2Fk8sgpt.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fk8sgpt-ai%2Fk8sgpt?ref=badge_large)

View File

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

View File

@@ -41,7 +41,10 @@ var addCmd = &cobra.Command{
_ = cmd.MarkFlagRequired("engine")
_ = cmd.MarkFlagRequired("baseurl")
}
if strings.ToLower(backend) == "amazonsagemaker" {
_ = cmd.MarkFlagRequired("endpointname")
_ = cmd.MarkFlagRequired("providerRegion")
}
},
Run: func(cmd *cobra.Command, args []string) {
@@ -90,6 +93,10 @@ var addCmd = &cobra.Command{
color.Red("Error: temperature ranges from 0 to 1.")
os.Exit(1)
}
if topP > 1.0 || topP < 0.0 {
color.Red("Error: topP ranges from 0 to 1.")
os.Exit(1)
}
if ai.NeedPassword(backend) && password == "" {
fmt.Printf("Enter %s Key: ", backend)
@@ -108,9 +115,12 @@ var addCmd = &cobra.Command{
Model: model,
Password: password,
BaseURL: baseURL,
EndpointName: endpointName,
Engine: engine,
Temperature: temperature,
ProviderRegion: providerRegion,
TopP: topP,
MaxTokens: maxTokens,
}
if providerIndex == -1 {
@@ -138,10 +148,16 @@ func init() {
addCmd.Flags().StringVarP(&password, "password", "p", "", "Backend AI password")
// add flag for url
addCmd.Flags().StringVarP(&baseURL, "baseurl", "u", "", "URL AI provider, (e.g `http://localhost:8080/v1`)")
// add flag for endpointName
addCmd.Flags().StringVarP(&endpointName, "endpointname", "n", "", "Endpoint Name, e.g. `endpoint-xxxxxxxxxxxx` (only for amazonbedrock, amazonsagemaker backends)")
// add flag for topP
addCmd.Flags().Float32VarP(&topP, "topp", "c", 0.5, "Probability Cutoff: Set a threshold (0.0-1.0) to limit word choices. Higher values add randomness, lower values increase predictability.")
// max tokens
addCmd.Flags().IntVarP(&maxTokens, "maxtokens", "l", 2048, "Specify a maximum output length. Adjust (1-...) to control text length. Higher values produce longer output, lower values limit length")
// add flag for temperature
addCmd.Flags().Float32VarP(&temperature, "temperature", "t", 0.7, "The sampling temperature, value ranges between 0 ( output be more deterministic) and 1 (more random)")
// add flag for azure open ai engine/deployment name
addCmd.Flags().StringVarP(&engine, "engine", "e", "", "Azure AI deployment name")
addCmd.Flags().StringVarP(&engine, "engine", "e", "", "Azure AI deployment name (only for azureopenai backend)")
//add flag for amazonbedrock region name
addCmd.Flags().StringVarP(&providerRegion, "providerRegion", "r", "", "Provider Region name")
addCmd.Flags().StringVarP(&providerRegion, "providerRegion", "r", "", "Provider Region name (only for amazonbedrock backend)")
}

View File

@@ -22,10 +22,13 @@ var (
backend string
password string
baseURL string
endpointName string
model string
engine string
temperature float32
providerRegion string
topP float32
maxTokens int
)
var configAI ai.AIConfiguration

View File

@@ -23,23 +23,26 @@ import (
)
var removeCmd = &cobra.Command{
Use: "remove [backend(s)]",
Short: "Remove a provider",
Long: "The command to remove an AI backend provider",
Args: cobra.ExactArgs(1),
Use: "remove",
Short: "Remove provider(s)",
Long: "The command to remove AI backend provider(s)",
PreRun: func(cmd *cobra.Command, args []string) {
_ = cmd.MarkFlagRequired("backends")
},
Run: func(cmd *cobra.Command, args []string) {
inputBackends := strings.Split(args[0], ",")
if backend == "" {
color.Red("Error: backends must be set.")
_ = cmd.Help()
return
}
inputBackends := strings.Split(backend, ",")
err := viper.UnmarshalKey("ai", &configAI)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
if len(inputBackends) == 0 {
color.Red("Error: backend must be set.")
os.Exit(1)
}
for _, b := range inputBackends {
foundBackend := false
for i, provider := range configAI.Providers {
@@ -54,11 +57,11 @@ var removeCmd = &cobra.Command{
}
}
if !foundBackend {
color.Red("Error: %s does not exist in configuration file. Please use k8sgpt auth new.", backend)
color.Red("Error: %s does not exist in configuration file. Please use k8sgpt auth new.", b)
os.Exit(1)
}
}
viper.Set("ai", configAI)
if err := viper.WriteConfig(); err != nil {
color.Red("Error writing config file: %s", err.Error())
@@ -67,3 +70,8 @@ var removeCmd = &cobra.Command{
},
}
func init() {
// add flag for backends
removeCmd.Flags().StringVarP(&backend, "backends", "b", "", "Backend AI providers to remove (separated by a comma)")
}

22
cmd/cache/add.go vendored
View File

@@ -28,20 +28,31 @@ var (
bucketName string
storageAccount string
containerName string
projectId string
)
// addCmd represents the add command
var addCmd = &cobra.Command{
Use: "add",
Use: "add [cache type]",
Short: "Add a remote cache",
Long: `This command allows you to add a remote cache to store the results of an analysis.
The supported cache types are:
- Azure Blob storage
- Google Cloud storage
- S3`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
color.Red("Error: Please provide a value for cache types. Run k8sgpt cache add --help")
os.Exit(1)
}
fmt.Println(color.YellowString("Adding remote based cache"))
remoteCache := cache.NewCacheProvider(bucketname, region, storageAccount, containerName)
err := cache.AddRemoteCache(remoteCache)
cacheType := args[0]
remoteCache, err := cache.NewCacheProvider(cacheType, bucketname, region, storageAccount, containerName, projectId)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
err = cache.AddRemoteCache(remoteCache)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
@@ -51,9 +62,10 @@ var addCmd = &cobra.Command{
func init() {
CacheCmd.AddCommand(addCmd)
addCmd.Flags().StringVarP(&region, "region", "r", "", "The region to use for the AWS S3 cache")
addCmd.Flags().StringVarP(&region, "region", "r", "", "The region to use for the AWS S3 or GCS cache")
addCmd.Flags().StringVarP(&bucketname, "bucket", "b", "", "The name of the AWS S3 bucket to use for the cache")
addCmd.MarkFlagsRequiredTogether("region", "bucket")
addCmd.Flags().StringVarP(&projectId, "projectid", "p", "", "The GCP project ID")
addCmd.Flags().StringVarP(&storageAccount, "storageacc", "s", "", "The Azure storage account name of the container")
addCmd.Flags().StringVarP(&containerName, "container", "c", "", "The Azure container name to use for the cache")
addCmd.MarkFlagsRequiredTogether("storageacc", "container")
@@ -62,4 +74,6 @@ func init() {
addCmd.MarkFlagsMutuallyExclusive("region", "container")
addCmd.MarkFlagsMutuallyExclusive("bucket", "storageacc")
addCmd.MarkFlagsMutuallyExclusive("bucket", "container")
addCmd.MarkFlagsMutuallyExclusive("projectid", "storageacc")
addCmd.MarkFlagsMutuallyExclusive("projectid", "container")
}

5
cmd/cache/cache.go vendored
View File

@@ -28,7 +28,10 @@ var CacheCmd = &cobra.Command{
Short: "For working with the cache the results of an analysis",
Long: `Cache commands allow you to add a remote cache, list the contents of the cache, and remove items from the cache.`,
Run: func(cmd *cobra.Command, args []string) {
cmd.Help()
err := cmd.Help()
if err != nil {
panic(err)
}
},
}

22
cmd/cache/list.go vendored
View File

@@ -16,9 +16,11 @@ package cache
import (
"os"
"reflect"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
)
@@ -30,22 +32,32 @@ var listCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
// load remote cache if it is configured
remoteCacheEnabled, err := cache.RemoteCacheEnabled()
c, err := cache.GetCacheConfiguration()
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
c := cache.New(false, remoteCacheEnabled)
// list the contents of the cache
names, err := c.List()
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
for _, name := range names {
println(name)
var headers []string
obj := cache.CacheObjectDetails{}
objType := reflect.TypeOf(obj)
for i := 0; i < objType.NumField(); i++ {
field := objType.Field(i)
headers = append(headers, field.Name)
}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader(headers)
for _, v := range names {
table.Append([]string{v.Name, v.UpdatedAt.String()})
}
table.Render()
},
}

54
cmd/cache/purge.go vendored Normal file
View File

@@ -0,0 +1,54 @@
/*
Copyright 2023 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cache
import (
"fmt"
"os"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
"github.com/spf13/cobra"
)
var purgeCmd = &cobra.Command{
Use: "purge [object name]",
Short: "Purge a remote cache",
Long: "This command allows you to delete/purge one object from the cache",
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
color.Red("Error: Please provide a value for object name. Run k8sgpt cache purge --help")
os.Exit(1)
}
objectKey := args[0]
fmt.Println(color.YellowString("Purging a remote cache."))
c, err := cache.GetCacheConfiguration()
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
err = c.Remove(objectKey)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
fmt.Println(color.GreenString("Object deleted."))
},
}
func init() {
CacheCmd.AddCommand(purgeCmd)
}

View File

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

View File

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

View File

@@ -33,6 +33,7 @@ var (
port string
metricsPort string
backend string
enableHttp bool
)
var ServeCmd = &cobra.Command{
@@ -126,6 +127,7 @@ var ServeCmd = &cobra.Command{
Backend: aiProvider.Name,
Port: port,
MetricsPort: metricsPort,
EnableHttp: enableHttp,
Token: aiProvider.Password,
Logger: logger,
}
@@ -153,4 +155,5 @@ func init() {
ServeCmd.Flags().StringVarP(&port, "port", "p", "8080", "Port to run the server on")
ServeCmd.Flags().StringVarP(&metricsPort, "metrics-port", "", "8081", "Port to run the metrics-server on")
ServeCmd.Flags().StringVarP(&backend, "backend", "b", "openai", "Backend AI provider")
ServeCmd.Flags().BoolVarP(&enableHttp, "http", "h", false, "Enable REST/http using gppc-gateway")
}

View File

@@ -9,7 +9,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
FROM golang:1.20.4-alpine3.16 AS builder
FROM golang:1.21-alpine3.19 AS builder
ENV CGO_ENABLED=0
ARG VERSION
@@ -36,4 +36,4 @@ WORKDIR /
COPY --from=builder /workspace/k8sgpt .
USER 65532:65532
ENTRYPOINT ["/k8sgpt"]
ENTRYPOINT ["/k8sgpt"]

215
go.mod
View File

@@ -1,106 +1,152 @@
module github.com/k8sgpt-ai/k8sgpt
go 1.20
go 1.21
require (
github.com/aquasecurity/trivy-operator v0.15.1
github.com/fatih/color v1.15.0
github.com/aquasecurity/trivy-operator v0.17.1
github.com/fatih/color v1.16.0
github.com/magiconair/properties v1.8.7
github.com/mittwald/go-helm-client v0.12.3
github.com/sashabaranov/go-openai v1.16.0
github.com/schollz/progressbar/v3 v3.13.1
github.com/spf13/cobra v1.7.0
github.com/spf13/viper v1.17.0
github.com/mittwald/go-helm-client v0.12.5
github.com/prometheus/prometheus v1.8.2-0.20211119115433-692a54649ed7
github.com/sashabaranov/go-openai v1.18.2
github.com/schollz/progressbar/v3 v3.14.1
github.com/spf13/cobra v1.8.0
github.com/spf13/viper v1.18.2
github.com/stretchr/testify v1.8.4
golang.org/x/term v0.13.0
helm.sh/helm/v3 v3.13.0
k8s.io/api v0.28.3
k8s.io/apimachinery v0.28.3
k8s.io/client-go v0.28.3
k8s.io/kubectl v0.28.3
golang.org/x/term v0.16.0
helm.sh/helm/v3 v3.13.3
k8s.io/api v0.28.4
k8s.io/apimachinery v0.28.4
k8s.io/client-go v0.28.4
k8s.io/kubectl v0.28.4 // indirect
)
require github.com/adrg/xdg v0.4.0
require (
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.3.0-20231002095256-194bc640518b.1
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.31.0-20231002095256-194bc640518b.1
buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go v1.3.0-20240208090903-d4147b284845.2
buf.build/gen/go/k8sgpt-ai/k8sgpt/protocolbuffers/go v1.32.0-20240208090903-d4147b284845.1
cloud.google.com/go/storage v1.36.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0
github.com/aws/aws-sdk-go v1.46.5
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.1
github.com/aws/aws-sdk-go v1.49.21
github.com/cohere-ai/cohere-go v0.2.0
github.com/google/generative-ai-go v0.5.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1
github.com/olekukonko/tablewriter v0.0.5
github.com/pterm/pterm v0.12.74
google.golang.org/api v0.155.0
gopkg.in/yaml.v2 v2.4.0
sigs.k8s.io/controller-runtime v0.16.3
sigs.k8s.io/gateway-api v1.0.0
)
require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 // indirect
github.com/Microsoft/hcsshim v0.11.0 // indirect
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect
atomicgo.dev/cursor v0.2.0 // indirect
atomicgo.dev/keyboard v0.2.9 // indirect
atomicgo.dev/schedule v0.1.0 // indirect
cloud.google.com/go v0.111.0 // indirect
cloud.google.com/go/ai v0.3.0 // indirect
cloud.google.com/go/compute v1.23.3 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v1.1.5 // indirect
cloud.google.com/go/longrunning v0.5.4 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0 // indirect
github.com/Microsoft/hcsshim v0.11.4 // indirect
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
github.com/anchore/go-struct-converter v0.0.0-20230627203149-c72ef8859ca9 // indirect
github.com/cohere-ai/tokenizer v1.1.1 // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/containerd/console v1.0.3 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/distribution/reference v0.5.0 // indirect
github.com/dlclark/regexp2 v1.10.0 // indirect
github.com/evanphx/json-patch/v5 v5.7.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
github.com/golang-jwt/jwt/v5 v5.0.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jpillora/backoff v1.0.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/lithammer/fuzzysearch v1.1.8 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/sagikazarmark/locafero v0.3.0 // indirect
github.com/prometheus/common/sigv4 v0.1.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect
go.opentelemetry.io/otel/metric v1.21.0 // indirect
google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe // indirect
gopkg.in/evanphx/json-patch.v5 v5.7.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
require (
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/Masterminds/squirrel v1.5.4 // indirect
github.com/aquasecurity/defsec v0.89.0 // indirect
github.com/aquasecurity/go-dep-parser v0.0.0-20230605080024-b71d9356a6c6 // indirect
github.com/aquasecurity/defsec v0.93.1 // indirect
github.com/aquasecurity/go-dep-parser v0.0.0-20231030050624-4548cca9a5c9 // indirect
github.com/aquasecurity/table v1.8.0 // indirect
github.com/aquasecurity/tml v0.6.1 // indirect
github.com/aquasecurity/trivy v0.42.1 // indirect
github.com/aquasecurity/trivy-db v0.0.0-20230515061101-378ab9ed302c // indirect
github.com/aquasecurity/trivy v0.47.0 // indirect
github.com/aquasecurity/trivy-db v0.0.0-20231020043206-3770774790ce // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/containerd/containerd v1.7.6 // indirect
github.com/containerd/containerd v1.7.11 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/cli v24.0.6+incompatible // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/docker v24.0.6+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/docker/cli v24.0.7+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v24.0.7+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.0 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/emicklei/go-restful/v3 v3.10.2 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/evanphx/json-patch v5.7.0+incompatible // indirect
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/logr v1.3.0 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonpointer v0.20.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-openapi/swag v0.22.4 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/gnostic v0.7.0
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/go-containerregistry v0.15.2 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-containerregistry v0.16.1 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.3.1 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/gosuri/uitable v0.0.4 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
@@ -108,12 +154,12 @@ require (
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/huandu/xstrings v1.4.0 // indirect
github.com/imdario/mergo v0.3.15 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jmoiron/sqlx v1.3.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.0 // indirect
github.com/klauspost/compress v1.17.2 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/lib/pq v1.10.9 // indirect
@@ -121,9 +167,8 @@ require (
github.com/mailru/easyjson v0.7.7 // indirect
github.com/masahiro331/go-xfs-filesystem v0.0.0-20230608043311-a335f4599b70 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
@@ -143,10 +188,10 @@ require (
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.17.0
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.11.1 // indirect
github.com/prometheus/client_golang v1.18.0
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/robfig/cron/v3 v3.0.1
github.com/rubenv/sql-migrate v1.5.2 // indirect
@@ -154,49 +199,47 @@ require (
github.com/samber/lo v1.38.1 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spdx/tools-golang v0.5.0 // indirect
github.com/spf13/afero v1.10.0 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spdx/tools-golang v0.5.3 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
go.opentelemetry.io/otel v1.14.0 // indirect
go.opentelemetry.io/otel/trace v1.14.0 // indirect
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
go.opentelemetry.io/otel v1.21.0 // indirect
go.opentelemetry.io/otel/trace v1.21.0 // indirect
go.starlark.net v0.0.0-20231016134836-22325403fcb3 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/oauth2 v0.12.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/grpc v1.58.3
google.golang.org/protobuf v1.31.0 // indirect
golang.org/x/crypto v0.18.0 // indirect
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
golang.org/x/net v0.20.0
golang.org/x/oauth2 v0.16.0 // indirect
golang.org/x/sync v0.5.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/grpc v1.61.0
google.golang.org/protobuf v1.32.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.28.2 // indirect
k8s.io/apiserver v0.28.2 // indirect
k8s.io/cli-runtime v0.28.3 // indirect
k8s.io/component-base v0.28.3 // indirect
k8s.io/apiextensions-apiserver v0.28.4
k8s.io/apiserver v0.28.4 // indirect
k8s.io/cli-runtime v0.28.4 // indirect
k8s.io/component-base v0.28.4 // indirect
k8s.io/klog/v2 v2.100.1 // indirect
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect
oras.land/oras-go v1.2.3 // indirect
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
oras.land/oras-go v1.2.4 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect
sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
sigs.k8s.io/kustomize/api v0.15.0 // indirect
sigs.k8s.io/kustomize/kyaml v0.15.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.0 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)
// v1.2.0 is taken from github.com/open-policy-agent/opa v0.42.0

1838
go.sum

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

View File

@@ -2,25 +2,21 @@ package ai
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"strings"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/bedrockruntime"
)
const amazonbedrockAIClientName = "amazonbedrock"
// AmazonBedRockClient represents the client for interacting with the Amazon Bedrock service.
type AmazonBedRockClient struct {
nopCloser
client *bedrockruntime.BedrockRuntime
language string
model string
temperature float32
}
@@ -91,8 +87,8 @@ func GetRegionOrDefault(region string) string {
return BEDROCK_DEFAULT_REGION
}
// Configure configures the AmazonBedRockClient with the provided configuration and language.
func (a *AmazonBedRockClient) Configure(config IAIConfig, language string) error {
// Configure configures the AmazonBedRockClient with the provided configuration.
func (a *AmazonBedRockClient) Configure(config IAIConfig) error {
// Create a new AWS session
providerRegion := GetRegionOrDefault(config.GetProviderRegion())
@@ -107,7 +103,6 @@ func (a *AmazonBedRockClient) Configure(config IAIConfig, language string) error
// Create a new BedrockRuntime client
a.client = bedrockruntime.New(sess)
a.language = language
a.model = GetModelOrDefault(config.GetModel())
a.temperature = config.GetTemperature()
@@ -115,7 +110,7 @@ func (a *AmazonBedRockClient) Configure(config IAIConfig, language string) error
}
// GetCompletion sends a request to the model for generating completion based on the provided prompt.
func (a *AmazonBedRockClient) GetCompletion(ctx context.Context, prompt string, promptTmpl string) (string, error) {
func (a *AmazonBedRockClient) GetCompletion(ctx context.Context, prompt string) (string, error) {
// Prepare the input data for the model invocation
request := map[string]interface{}{
@@ -152,45 +147,7 @@ func (a *AmazonBedRockClient) GetCompletion(ctx context.Context, prompt string,
return output.Completion, nil
}
// Parse generates a completion for the provided prompt using the Amazon Bedrock model.
func (a *AmazonBedRockClient) Parse(ctx context.Context, prompt []string, cache cache.ICache, promptTmpl string) (string, error) {
inputKey := strings.Join(prompt, " ")
// Check for cached data
cacheKey := util.GetCacheKey(a.GetName(), a.language, inputKey)
if !cache.IsCacheDisabled() && cache.Exists(cacheKey) {
response, err := cache.Load(cacheKey)
if err != nil {
return "", err
}
if response != "" {
output, err := base64.StdEncoding.DecodeString(response)
if err != nil {
color.Red("error decoding cached data: %v", err)
return "", nil
}
return string(output), nil
}
}
response, err := a.GetCompletion(ctx, inputKey, promptTmpl)
if err != nil {
return "", err
}
err = cache.Store(cacheKey, base64.StdEncoding.EncodeToString([]byte(response)))
if err != nil {
color.Red("error storing value to cache: %v", err)
return "", nil
}
return response, nil
}
// GetName returns the name of the AmazonBedRockClient.
func (a *AmazonBedRockClient) GetName() string {
return "amazonbedrock"
return amazonbedrockAIClientName
}

137
pkg/ai/amazonsagemaker.go Normal file
View File

@@ -0,0 +1,137 @@
/*
Copyright 2023 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ai
import (
"context"
"encoding/json"
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/sagemakerruntime"
)
const amazonsagemakerAIClientName = "amazonsagemaker"
type SageMakerAIClient struct {
nopCloser
client *sagemakerruntime.SageMakerRuntime
model string
temperature float32
endpoint string
topP float32
maxTokens int
}
type Generations []struct {
Generation struct {
Role string `json:"role"`
Content string `json:"content"`
} `json:"generation"`
}
type Request struct {
Inputs [][]Message `json:"inputs"`
Parameters Parameters `json:"parameters"`
}
type Message struct {
Role string `json:"role"`
Content string `json:"content"`
}
type Parameters struct {
MaxNewTokens int `json:"max_new_tokens"`
TopP float64 `json:"top_p"`
Temperature float64 `json:"temperature"`
}
func (c *SageMakerAIClient) Configure(config IAIConfig) error {
// Create a new AWS session
sess := session.Must(session.NewSessionWithOptions(session.Options{
Config: aws.Config{Region: aws.String(config.GetProviderRegion())},
SharedConfigState: session.SharedConfigEnable,
}))
// Create a new SageMaker runtime client
c.client = sagemakerruntime.New(sess)
c.model = config.GetModel()
c.endpoint = config.GetEndpointName()
c.temperature = config.GetTemperature()
c.maxTokens = config.GetMaxTokens()
c.topP = config.GetTopP()
return nil
}
func (c *SageMakerAIClient) GetCompletion(_ context.Context, prompt string) (string, error) {
// Create a completion request
request := Request{
Inputs: [][]Message{
{
{Role: "system", Content: "DEFAULT_PROMPT"},
{Role: "user", Content: prompt},
},
},
Parameters: Parameters{
MaxNewTokens: int(c.maxTokens),
TopP: float64(c.topP),
Temperature: float64(c.temperature),
},
}
// Convert request to []byte
bytesData, err := json.Marshal(request)
if err != nil {
return "", err
}
// Create an input object
input := &sagemakerruntime.InvokeEndpointInput{
Body: bytesData,
EndpointName: aws.String(c.endpoint),
ContentType: aws.String("application/json"), // Set the content type as per your model's requirements
Accept: aws.String("application/json"), // Set the accept type as per your model's requirements
CustomAttributes: aws.String("accept_eula=true"),
}
// Call the InvokeEndpoint function
result, err := c.client.InvokeEndpoint(input)
if err != nil {
return "", err
}
// // Define a slice of Generations
var generations Generations
err = json.Unmarshal([]byte(string(result.Body)), &generations)
if err != nil {
return "", err
}
// Check for length of generations
if len(generations) != 1 {
return "", fmt.Errorf("Expected exactly one generation, but got %d", len(generations))
}
// Access the content
content := generations[0].Generation.Content
return content, nil
}
func (c *SageMakerAIClient) GetName() string {
return amazonsagemakerAIClientName
}

View File

@@ -2,27 +2,22 @@ package ai
import (
"context"
"encoding/base64"
"errors"
"fmt"
"strings"
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
"github.com/fatih/color"
"github.com/sashabaranov/go-openai"
)
const azureAIClientName = "azureopenai"
type AzureAIClient struct {
nopCloser
client *openai.Client
language string
model string
temperature float32
}
func (c *AzureAIClient) Configure(config IAIConfig, lang string) error {
func (c *AzureAIClient) Configure(config IAIConfig) error {
token := config.GetPassword()
baseURL := config.GetBaseURL()
engine := config.GetEngine()
@@ -40,21 +35,20 @@ func (c *AzureAIClient) Configure(config IAIConfig, lang string) error {
if client == nil {
return errors.New("error creating Azure OpenAI client")
}
c.language = lang
c.client = client
c.model = config.GetModel()
c.temperature = config.GetTemperature()
return nil
}
func (c *AzureAIClient) GetCompletion(ctx context.Context, prompt string, promptTmpl string) (string, error) {
func (c *AzureAIClient) GetCompletion(ctx context.Context, prompt string) (string, error) {
// Create a completion request
resp, err := c.client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{
Model: c.model,
Messages: []openai.ChatCompletionMessage{
{
Role: openai.ChatMessageRoleUser,
Content: fmt.Sprintf(default_prompt, c.language, prompt),
Content: prompt,
},
},
Temperature: c.temperature,
@@ -65,42 +59,6 @@ func (c *AzureAIClient) GetCompletion(ctx context.Context, prompt string, prompt
return resp.Choices[0].Message.Content, nil
}
func (a *AzureAIClient) Parse(ctx context.Context, prompt []string, cache cache.ICache, promptTmpl string) (string, error) {
inputKey := strings.Join(prompt, " ")
// Check for cached data
cacheKey := util.GetCacheKey(a.GetName(), a.language, inputKey)
if !cache.IsCacheDisabled() && cache.Exists(cacheKey) {
response, err := cache.Load(cacheKey)
if err != nil {
return "", err
}
if response != "" {
output, err := base64.StdEncoding.DecodeString(response)
if err != nil {
color.Red("error decoding cached data: %v", err)
return "", nil
}
return string(output), nil
}
}
response, err := a.GetCompletion(ctx, inputKey, promptTmpl)
if err != nil {
return "", err
}
err = cache.Store(cacheKey, base64.StdEncoding.EncodeToString([]byte(response)))
if err != nil {
color.Red("error storing value to cache: %v", err)
return "", nil
}
return response, nil
}
func (a *AzureAIClient) GetName() string {
return "azureopenai"
func (c *AzureAIClient) GetName() string {
return azureAIClientName
}

View File

@@ -15,26 +15,22 @@ package ai
import (
"context"
"encoding/base64"
"errors"
"fmt"
"strings"
"github.com/cohere-ai/cohere-go"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
)
const cohereAIClientName = "cohere"
type CohereClient struct {
nopCloser
client *cohere.Client
language string
model string
temperature float32
}
func (c *CohereClient) Configure(config IAIConfig, language string) error {
func (c *CohereClient) Configure(config IAIConfig) error {
token := config.GetPassword()
client, err := cohere.CreateClient(token)
@@ -50,21 +46,17 @@ func (c *CohereClient) Configure(config IAIConfig, language string) error {
if client == nil {
return errors.New("error creating Cohere client")
}
c.language = language
c.client = client
c.model = config.GetModel()
c.temperature = config.GetTemperature()
return nil
}
func (c *CohereClient) GetCompletion(ctx context.Context, prompt, promptTmpl string) (string, error) {
func (c *CohereClient) GetCompletion(_ context.Context, prompt string) (string, error) {
// Create a completion request
if len(promptTmpl) == 0 {
promptTmpl = PromptMap["default"]
}
resp, err := c.client.Generate(cohere.GenerateOptions{
Model: c.model,
Prompt: fmt.Sprintf(strings.TrimSpace(promptTmpl), c.language, prompt),
Prompt: prompt,
MaxTokens: cohere.Uint(2048),
Temperature: cohere.Float64(float64(c.temperature)),
K: cohere.Int(0),
@@ -77,42 +69,6 @@ func (c *CohereClient) GetCompletion(ctx context.Context, prompt, promptTmpl str
return resp.Generations[0].Text, nil
}
func (a *CohereClient) Parse(ctx context.Context, prompt []string, cache cache.ICache, promptTmpl string) (string, error) {
inputKey := strings.Join(prompt, " ")
// Check for cached data
cacheKey := util.GetCacheKey(a.GetName(), a.language, inputKey)
if !cache.IsCacheDisabled() && cache.Exists(cacheKey) {
response, err := cache.Load(cacheKey)
if err != nil {
return "", err
}
if response != "" {
output, err := base64.StdEncoding.DecodeString(response)
if err != nil {
color.Red("error decoding cached data: %v", err)
return "", nil
}
return string(output), nil
}
}
response, err := a.GetCompletion(ctx, inputKey, promptTmpl)
if err != nil {
return "", err
}
err = cache.Store(cacheKey, base64.StdEncoding.EncodeToString([]byte(response)))
if err != nil {
color.Red("error storing value to cache: %v", err)
return "", nil
}
return response, nil
}
func (a *CohereClient) GetName() string {
return "cohere"
func (c *CohereClient) GetName() string {
return cohereAIClientName
}

119
pkg/ai/googlegenai.go Normal file
View File

@@ -0,0 +1,119 @@
/*
Copyright 2023 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ai
import (
"context"
"errors"
"fmt"
"github.com/fatih/color"
"github.com/google/generative-ai-go/genai"
"google.golang.org/api/option"
)
const googleAIClientName = "google"
type GoogleGenAIClient struct {
client *genai.Client
model string
temperature float32
topP float32
maxTokens int
}
func (c *GoogleGenAIClient) Configure(config IAIConfig) error {
ctx := context.Background()
// Access your API key as an environment variable (see "Set up your API key" above)
token := config.GetPassword()
authOption := option.WithAPIKey(token)
if token[0] == '{' {
authOption = option.WithCredentialsJSON([]byte(token))
}
client, err := genai.NewClient(ctx, authOption)
if err != nil {
return fmt.Errorf("creating genai Google SDK client: %w", err)
}
c.client = client
c.model = config.GetModel()
c.temperature = config.GetTemperature()
c.topP = config.GetTopP()
c.maxTokens = config.GetMaxTokens()
return nil
}
func (c *GoogleGenAIClient) GetCompletion(ctx context.Context, prompt string) (string, error) {
// Available models are at https://ai.google.dev/models e.g.gemini-pro.
model := c.client.GenerativeModel(c.model)
model.SetTemperature(c.temperature)
model.SetTopP(c.topP)
model.SetMaxOutputTokens(int32(c.maxTokens))
// Google AI SDK is capable of different inputs than just text, for now set explicit text prompt type.
// Similarly, we could stream the response. For now k8sgpt does not support streaming.
resp, err := model.GenerateContent(ctx, genai.Text(prompt))
if err != nil {
return "", err
}
if len(resp.Candidates) == 0 {
if resp.PromptFeedback.BlockReason == genai.BlockReasonSafety {
for _, r := range resp.PromptFeedback.SafetyRatings {
if !r.Blocked {
continue
}
return "", fmt.Errorf("complection blocked due to %v with probability %v", r.Category.String(), r.Probability.String())
}
}
return "", errors.New("no complection returned; unknown reason")
}
// Format output.
// TODO(bwplotka): Provider richer output in certain cases e.g. suddenly finished
// completion based on finish reasons or safety rankings.
got := resp.Candidates[0]
var output string
for _, part := range got.Content.Parts {
switch o := part.(type) {
case genai.Text:
output += string(o)
output += "\n"
default:
color.Yellow("found unsupported AI response part of type %T; ignoring", part)
}
}
if got.CitationMetadata != nil && len(got.CitationMetadata.CitationSources) > 0 {
output += "Citations:\n"
for _, source := range got.CitationMetadata.CitationSources {
// TODO(bwplotka): Give details around what exactly words could be attributed to the citation.
output += fmt.Sprintf("* %s, %s\n", *source.URI, source.License)
}
}
return output, nil
}
func (c *GoogleGenAIClient) GetName() string {
return googleAIClientName
}
func (c *GoogleGenAIClient) Close() {
if err := c.client.Close(); err != nil {
color.Red("googleai client close error: %v", err)
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,22 +15,17 @@ package ai
import (
"context"
"encoding/base64"
"errors"
"fmt"
"strings"
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
"github.com/sashabaranov/go-openai"
"github.com/fatih/color"
)
const openAIClientName = "openai"
type OpenAIClient struct {
nopCloser
client *openai.Client
language string
model string
temperature float32
}
@@ -43,7 +38,7 @@ const (
topP = 1.0
)
func (c *OpenAIClient) Configure(config IAIConfig, language string) error {
func (c *OpenAIClient) Configure(config IAIConfig) error {
token := config.GetPassword()
defaultConfig := openai.DefaultConfig(token)
@@ -56,24 +51,20 @@ func (c *OpenAIClient) Configure(config IAIConfig, language string) error {
if client == nil {
return errors.New("error creating OpenAI client")
}
c.language = language
c.client = client
c.model = config.GetModel()
c.temperature = config.GetTemperature()
return nil
}
func (c *OpenAIClient) GetCompletion(ctx context.Context, prompt string, promptTmpl string) (string, error) {
func (c *OpenAIClient) GetCompletion(ctx context.Context, prompt string) (string, error) {
// Create a completion request
if len(promptTmpl) == 0 {
promptTmpl = PromptMap["default"]
}
resp, err := c.client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{
Model: c.model,
Messages: []openai.ChatCompletionMessage{
{
Role: "user",
Content: fmt.Sprintf(promptTmpl, c.language, prompt),
Content: prompt,
},
},
Temperature: c.temperature,
@@ -88,42 +79,6 @@ func (c *OpenAIClient) GetCompletion(ctx context.Context, prompt string, promptT
return resp.Choices[0].Message.Content, nil
}
func (a *OpenAIClient) Parse(ctx context.Context, prompt []string, cache cache.ICache, promptTmpl string) (string, error) {
inputKey := strings.Join(prompt, " ")
// Check for cached data
cacheKey := util.GetCacheKey(a.GetName(), a.language, inputKey)
if !cache.IsCacheDisabled() && cache.Exists(cacheKey) {
response, err := cache.Load(cacheKey)
if err != nil {
return "", err
}
if response != "" {
output, err := base64.StdEncoding.DecodeString(response)
if err != nil {
color.Red("error decoding cached data: %v", err)
return "", nil
}
return string(output), nil
}
}
response, err := a.GetCompletion(ctx, inputKey, promptTmpl)
if err != nil {
return "", err
}
err = cache.Store(cacheKey, base64.StdEncoding.EncodeToString([]byte(response)))
if err != nil {
color.Red("error storing value to cache: %v", err)
return "", nil
}
return response, nil
}
func (a *OpenAIClient) GetName() string {
return "openai"
func (c *OpenAIClient) GetName() string {
return openAIClientName
}

View File

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

View File

@@ -15,9 +15,9 @@ package analysis
import (
"context"
"encoding/base64"
"errors"
"fmt"
"os"
"reflect"
"strings"
"sync"
@@ -38,6 +38,7 @@ type Analysis struct {
Context context.Context
Filters []string
Client *kubernetes.Client
Language string
AIClient ai.IAI
Results []common.Result
Errors []string
@@ -65,17 +66,58 @@ type JsonOutput struct {
Results []common.Result `json:"results"`
}
func NewAnalysis(backend string, language string, filters []string, namespace string, noCache bool, explain bool, maxConcurrency int, withDoc bool) (*Analysis, error) {
var configAI ai.AIConfiguration
err := viper.UnmarshalKey("ai", &configAI)
func NewAnalysis(
backend string,
language string,
filters []string,
namespace string,
noCache bool,
explain bool,
maxConcurrency int,
withDoc bool,
interactiveMode bool,
) (*Analysis, error) {
// Get kubernetes client from viper.
kubecontext := viper.GetString("kubecontext")
kubeconfig := viper.GetString("kubeconfig")
client, err := kubernetes.NewClient(kubecontext, kubeconfig)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
return nil, fmt.Errorf("initialising kubernetes client: %w", err)
}
if len(configAI.Providers) == 0 && explain {
color.Red("Error: AI provider not specified in configuration. Please run k8sgpt auth")
os.Exit(1)
// Load remote cache if it is configured.
cache, err := cache.GetCacheConfiguration()
if err != nil {
return nil, err
}
if noCache {
cache.DisableCache()
}
a := &Analysis{
Context: context.Background(),
Filters: filters,
Client: client,
Language: language,
Namespace: namespace,
Cache: cache,
Explain: explain,
MaxConcurrency: maxConcurrency,
WithDoc: withDoc,
}
if !explain {
// Return early if AI use was not requested.
return a, nil
}
var configAI ai.AIConfiguration
if err := viper.UnmarshalKey("ai", &configAI); err != nil {
return nil, err
}
if len(configAI.Providers) == 0 {
return nil, errors.New("AI provider not specified in configuration. Please run k8sgpt auth")
}
// Backend string will have high priority than a default provider
@@ -93,45 +135,16 @@ func NewAnalysis(backend string, language string, filters []string, namespace st
}
if aiProvider.Name == "" {
color.Red("Error: AI provider %s not specified in configuration. Please run k8sgpt auth", backend)
return nil, errors.New("AI provider not specified in configuration")
return nil, fmt.Errorf("AI provider %s not specified in configuration. Please run k8sgpt auth", backend)
}
aiClient := ai.NewClient(aiProvider.Name)
if err := aiClient.Configure(&aiProvider, language); err != nil {
color.Red("Error: %v", err)
if err := aiClient.Configure(&aiProvider); err != nil {
return nil, err
}
ctx := context.Background()
// Get kubernetes client from viper
kubecontext := viper.GetString("kubecontext")
kubeconfig := viper.GetString("kubeconfig")
client, err := kubernetes.NewClient(kubecontext, kubeconfig)
if err != nil {
color.Red("Error initialising kubernetes client: %v", err)
return nil, err
}
// load remote cache if it is configured
remoteCacheEnabled, err := cache.RemoteCacheEnabled()
if err != nil {
return nil, err
}
return &Analysis{
Context: ctx,
Filters: filters,
Client: client,
AIClient: aiClient,
Namespace: namespace,
Cache: cache.New(noCache, remoteCacheEnabled),
Explain: explain,
MaxConcurrency: maxConcurrency,
AnalysisAIProvider: backend,
WithDoc: withDoc,
}, nil
a.AIClient = aiClient
a.AnalysisAIProvider = aiProvider.Name
return a, nil
}
func (a *Analysis) RunAnalysis() {
@@ -261,14 +274,14 @@ func (a *Analysis) GetAIResults(output string, anonymize bool) error {
}
texts = append(texts, failure.Text)
}
// If the resource `Kind` comes from a "integration plugin", maybe a customized prompt template will be involved.
var promptTemplate string
promptTemplate := ai.PromptMap["default"]
// If the resource `Kind` comes from an "integration plugin",
// maybe a customized prompt template will be involved.
if prompt, ok := ai.PromptMap[analysis.Kind]; ok {
promptTemplate = prompt
} else {
promptTemplate = ai.PromptMap["default"]
}
parsedText, err := a.AIClient.Parse(a.Context, texts, a.Cache, promptTemplate)
result, err := a.getAIResultForSanitizedFailures(texts, promptTemplate)
if err != nil {
// FIXME: can we avoid checking if output is json multiple times?
// maybe implement the progress bar better?
@@ -276,23 +289,22 @@ func (a *Analysis) GetAIResults(output string, anonymize bool) error {
_ = bar.Exit()
}
// Check for exhaustion
// Check for exhaustion.
if strings.Contains(err.Error(), "status code: 429") {
return fmt.Errorf("exhausted API quota for AI provider %s: %v", a.AIClient.GetName(), err)
} else {
return fmt.Errorf("failed while calling AI provider %s: %v", a.AIClient.GetName(), err)
}
return fmt.Errorf("failed while calling AI provider %s: %v", a.AIClient.GetName(), err)
}
if anonymize {
for _, failure := range analysis.Error {
for _, s := range failure.Sensitive {
parsedText = strings.ReplaceAll(parsedText, s.Masked, s.Unmasked)
result = strings.ReplaceAll(result, s.Masked, s.Unmasked)
}
}
}
analysis.Details = parsedText
analysis.Details = result
if output != "json" {
_ = bar.Add(1)
}
@@ -300,3 +312,44 @@ func (a *Analysis) GetAIResults(output string, anonymize bool) error {
}
return nil
}
func (a *Analysis) getAIResultForSanitizedFailures(texts []string, promptTmpl string) (string, error) {
inputKey := strings.Join(texts, " ")
// Check for cached data.
// TODO(bwplotka): This might depend on model too (or even other client configuration pieces), fix it in later PRs.
cacheKey := util.GetCacheKey(a.AIClient.GetName(), a.Language, inputKey)
if !a.Cache.IsCacheDisabled() && a.Cache.Exists(cacheKey) {
response, err := a.Cache.Load(cacheKey)
if err != nil {
return "", err
}
if response != "" {
output, err := base64.StdEncoding.DecodeString(response)
if err == nil {
return string(output), nil
}
color.Red("error decoding cached data; ignoring cache item: %v", err)
}
}
// Process template.
prompt := fmt.Sprintf(strings.TrimSpace(promptTmpl), a.Language, inputKey)
response, err := a.AIClient.GetCompletion(a.Context, prompt)
if err != nil {
return "", err
}
if err = a.Cache.Store(cacheKey, base64.StdEncoding.EncodeToString([]byte(response))); err != nil {
color.Red("error storing value to cache; value won't be cached: %v", err)
}
return response, nil
}
func (a *Analysis) Close() {
if a.AIClient == nil {
return
}
a.AIClient.Close()
}

View File

@@ -58,8 +58,12 @@ func (a *Analysis) jsonOutput() ([]byte, error) {
func (a *Analysis) textOutput() ([]byte, error) {
var output strings.Builder
// Print the AI provider used for this analysis
output.WriteString(fmt.Sprintf("AI Provider: %s\n", color.YellowString(a.AnalysisAIProvider)))
// Print the AI provider used for this analysis (if explain was enabled).
if a.Explain {
output.WriteString(fmt.Sprintf("AI Provider: %s\n", color.YellowString(a.AnalysisAIProvider)))
} else {
output.WriteString(fmt.Sprintf("AI Provider: %s\n", color.YellowString("AI not used; --explain not set")))
}
if len(a.Errors) != 0 {
output.WriteString("\n")

View File

@@ -49,6 +49,10 @@ var additionalAnalyzerMap = map[string]common.IAnalyzer{
"HorizontalPodAutoScaler": HpaAnalyzer{},
"PodDisruptionBudget": PdbAnalyzer{},
"NetworkPolicy": NetworkPolicyAnalyzer{},
"Log": LogAnalyzer{},
"GatewayClass": GatewayClassAnalyzer{},
"Gateway": GatewayAnalyzer{},
"HTTPRoute": HTTPRouteAnalyzer{},
}
func ListFilters() ([]string, []string, []string) {

108
pkg/analyzer/gateway.go Normal file
View File

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

View File

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

View File

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

View File

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

228
pkg/analyzer/httroute.go Normal file
View File

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

View File

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

View File

@@ -98,26 +98,28 @@ func (IngressAnalyzer) Analyze(a common.Analyzer) ([]common.Result, error) {
// loop over rules
for _, rule := range ing.Spec.Rules {
// loop over paths
for _, path := range rule.HTTP.Paths {
_, err := a.Client.GetClient().CoreV1().Services(ing.Namespace).Get(a.Context, path.Backend.Service.Name, metav1.GetOptions{})
if err != nil {
doc := apiDoc.GetApiDocV2("spec.rules.http.paths.backend.service")
// loop over HTTP paths
if rule.HTTP != nil {
for _, path := range rule.HTTP.Paths {
_, err := a.Client.GetClient().CoreV1().Services(ing.Namespace).Get(a.Context, path.Backend.Service.Name, metav1.GetOptions{})
if err != nil {
doc := apiDoc.GetApiDocV2("spec.rules.http.paths.backend.service")
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Ingress uses the service %s/%s which does not exist.", ing.Namespace, path.Backend.Service.Name),
KubernetesDoc: doc,
Sensitive: []common.Sensitive{
{
Unmasked: ing.Namespace,
Masked: util.MaskString(ing.Namespace),
failures = append(failures, common.Failure{
Text: fmt.Sprintf("Ingress uses the service %s/%s which does not exist.", ing.Namespace, path.Backend.Service.Name),
KubernetesDoc: doc,
Sensitive: []common.Sensitive{
{
Unmasked: ing.Namespace,
Masked: util.MaskString(ing.Namespace),
},
{
Unmasked: path.Backend.Service.Name,
Masked: util.MaskString(path.Backend.Service.Name),
},
},
{
Unmasked: path.Backend.Service.Name,
Masked: util.MaskString(path.Backend.Service.Name),
},
},
})
})
}
}
}
}

116
pkg/analyzer/log.go Normal file
View File

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

View File

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

View File

@@ -9,7 +9,7 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
"github.com/spf13/viper"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob"
)
// Generate ICache implementation
@@ -20,6 +20,48 @@ type AzureCache struct {
session *azblob.Client
}
type AzureCacheConfiguration struct {
StorageAccount string `mapstructure:"storageaccount" yaml:"storageaccount,omitempty"`
ContainerName string `mapstructure:"container" yaml:"container,omitempty"`
}
func (s *AzureCache) Configure(cacheInfo CacheProvider) error {
s.ctx = context.Background()
if cacheInfo.Azure.ContainerName == "" {
log.Fatal("Azure Container name not configured")
}
if cacheInfo.Azure.StorageAccount == "" {
log.Fatal("Azure Storage account not configured")
}
// We assume that Storage account is already in place
blobUrl := fmt.Sprintf("https://%s.blob.core.windows.net/", cacheInfo.Azure.StorageAccount)
credential, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
log.Fatal(err)
}
client, err := azblob.NewClient(blobUrl, credential, nil)
if err != nil {
log.Fatal(err)
}
// Try to create the blob container
_, err = client.CreateContainer(s.ctx, cacheInfo.Azure.ContainerName, nil)
if err != nil {
// TODO: Maybe there is a better way to check this?
// docs: https://pkg.go.dev/github.com/Azure/azure-storage-blob-go/azblob
if strings.Contains(err.Error(), "ContainerAlreadyExists") {
// do nothing
} else {
return err
}
}
s.containerName = cacheInfo.Azure.ContainerName
s.session = client
return nil
}
func (s *AzureCache) Store(key string, data string) error {
// Store the object as a new file in the Azure blob storage with data as the content
cacheData := []byte(data)
@@ -45,9 +87,9 @@ func (s *AzureCache) Load(key string) (string, error) {
return data.String(), nil
}
func (s *AzureCache) List() ([]string, error) {
func (s *AzureCache) List() ([]CacheObjectDetails, error) {
// List the files in the blob containerName
files := []string{}
files := []CacheObjectDetails{}
pager := s.session.NewListBlobsFlatPager(s.containerName, &azblob.ListBlobsFlatOptions{
Include: azblob.ListBlobsInclude{Snapshots: false, Versions: false},
@@ -60,13 +102,24 @@ func (s *AzureCache) List() ([]string, error) {
}
for _, blob := range resp.Segment.BlobItems {
files = append(files, *blob.Name)
files = append(files, CacheObjectDetails{
Name: *blob.Name,
UpdatedAt: *blob.Properties.LastModified,
})
}
}
return files, nil
}
func (s *AzureCache) Remove(key string) error {
_, err := s.session.DeleteBlob(s.ctx, s.containerName, key, &blob.DeleteOptions{})
if err != nil {
return err
}
return nil
}
func (s *AzureCache) Exists(key string) bool {
// Check if the object exists in the blob storage
pager := s.session.NewListBlobsFlatPager(s.containerName, &azblob.ListBlobsFlatOptions{
@@ -93,46 +146,10 @@ func (s *AzureCache) IsCacheDisabled() bool {
return s.noCache
}
func NewAzureCache(nocache bool) ICache {
ctx := context.Background()
var cache CacheProvider
err := viper.UnmarshalKey("cache", &cache)
if err != nil {
panic(err)
}
if cache.ContainerName == "" {
log.Fatal("Azure Container name not configured")
}
if cache.StorageAccount == "" {
log.Fatal("Azure Storage account not configured")
}
// We assume that Storage account is already in place
blobUrl := fmt.Sprintf("https://%s.blob.core.windows.net/", cache.StorageAccount)
credential, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
log.Fatal(err)
}
client, err := azblob.NewClient(blobUrl, credential, nil)
if err != nil {
log.Fatal(err)
}
// Try to create the blob container
_, err = client.CreateContainer(ctx, cache.ContainerName, nil)
if err != nil {
// TODO: Maybe there is a better way to check this?
// docs: https://pkg.go.dev/github.com/Azure/azure-storage-blob-go/azblob
if strings.Contains(err.Error(), "ContainerAlreadyExists") {
// do nothing
} else {
log.Fatal(err)
}
}
return &AzureCache{
ctx: ctx,
noCache: nocache,
containerName: cache.ContainerName,
session: client,
}
func (s *AzureCache) GetName() string {
return "azure"
}
func (s *AzureCache) DisableCache() {
s.noCache = true
}

145
pkg/cache/cache.go vendored
View File

@@ -1,92 +1,108 @@
package cache
import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"fmt"
"github.com/spf13/viper"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type CacheType string
const (
Azure CacheType = "azure"
S3 CacheType = "s3"
FileBased CacheType = "file"
var (
types = []ICache{
&AzureCache{},
&FileBasedCache{},
&GCSCache{},
&S3Cache{},
}
)
type ICache interface {
Configure(cacheInfo CacheProvider) error
Store(key string, data string) error
Load(key string) (string, error)
List() ([]string, error)
List() ([]CacheObjectDetails, error)
Remove(key string) error
Exists(key string) bool
IsCacheDisabled() bool
GetName() string
DisableCache()
}
func New(noCache bool, remoteCache CacheType) ICache {
switch remoteCache {
case S3:
return NewS3Cache(noCache)
case Azure:
return NewAzureCache(noCache)
case FileBased:
return &FileBasedCache{
noCache: noCache,
}
default:
return &FileBasedCache{
noCache: noCache,
func New(cacheType string) ICache {
for _, t := range types {
if cacheType == t.GetName() {
return t
}
}
return &FileBasedCache{}
}
// CacheProvider is the configuration for the cache provider when using a remote cache
type CacheProvider struct {
BucketName string `mapstructure:"bucketname" yaml:"bucketname,omitempty"`
Region string `mapstructure:"region" yaml:"region,omitempty"`
StorageAccount string `mapstructure:"storageaccount" yaml:"storageaccount,omitempty"`
ContainerName string `mapstructure:"container" yaml:"container,omitempty"`
}
// NewCacheProvider constructs a new cache struct
func NewCacheProvider(bucketname, region, storageaccount, containername string) CacheProvider {
return CacheProvider{
BucketName: bucketname,
Region: region,
StorageAccount: storageaccount,
ContainerName: containername,
}
}
// If we have set a remote cache, return the remote cache type
func RemoteCacheEnabled() (CacheType, error) {
// load remote cache if it is configured
var cache CacheProvider
err := viper.UnmarshalKey("cache", &cache)
if err != nil {
return "", err
}
if cache.BucketName != "" && cache.Region != "" {
return S3, nil
} else if cache.StorageAccount != "" && cache.ContainerName != "" {
return Azure, nil
}
return FileBased, nil
}
func AddRemoteCache(cache CacheProvider) error {
func ParseCacheConfiguration() (CacheProvider, error) {
var cacheInfo CacheProvider
err := viper.UnmarshalKey("cache", &cacheInfo)
if err != nil {
return err
return cacheInfo, err
}
return cacheInfo, nil
}
func NewCacheProvider(cacheType, bucketname, region, storageAccount, containerName, projectId string) (CacheProvider, error) {
cProvider := CacheProvider{}
switch {
case cacheType == "azure":
cProvider.Azure.ContainerName = containerName
cProvider.Azure.StorageAccount = storageAccount
case cacheType == "gcs":
cProvider.GCS.BucketName = bucketname
cProvider.GCS.ProjectId = projectId
cProvider.GCS.Region = region
case cacheType == "s3":
cProvider.S3.BucketName = bucketname
cProvider.S3.Region = region
default:
return CacheProvider{}, status.Error(codes.Internal, fmt.Sprintf("%s is not a valid option", cacheType))
}
cacheInfo.BucketName = cache.BucketName
cacheInfo.Region = cache.Region
cacheInfo.StorageAccount = cache.StorageAccount
cacheInfo.ContainerName = cache.ContainerName
cache := New(cacheType)
err := cache.Configure(cProvider)
if err != nil {
return CacheProvider{}, err
}
return cProvider, nil
}
// If we have set a remote cache, return the remote cache configuration
func GetCacheConfiguration() (ICache, error) {
cacheInfo, err := ParseCacheConfiguration()
if err != nil {
return nil, err
}
var cache ICache
switch {
case cacheInfo.GCS != GCSCacheConfiguration{}:
cache = &GCSCache{}
case cacheInfo.Azure != AzureCacheConfiguration{}:
cache = &AzureCache{}
case cacheInfo.S3 != S3CacheConfiguration{}:
cache = &S3Cache{}
default:
cache = &FileBasedCache{}
}
err_config := cache.Configure(cacheInfo)
return cache, err_config
}
func AddRemoteCache(cacheInfo CacheProvider) error {
viper.Set("cache", cacheInfo)
err = viper.WriteConfig()
err := viper.WriteConfig()
if err != nil {
return err
}
@@ -99,9 +115,6 @@ func RemoveRemoteCache() error {
if err != nil {
return status.Error(codes.Internal, "cache unmarshal")
}
if cacheInfo.BucketName == "" && cacheInfo.ContainerName == "" && cacheInfo.StorageAccount == "" {
return status.Error(codes.Internal, "no remote cache configured")
}
cacheInfo = CacheProvider{}
viper.Set("cache", cacheInfo)

View File

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

133
pkg/cache/gcs_based.go vendored Normal file
View File

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

109
pkg/cache/s3_based.go vendored
View File

@@ -7,7 +7,6 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/spf13/viper"
)
// Generate ICache implementation
@@ -17,6 +16,45 @@ type S3Cache struct {
session *s3.S3
}
type S3CacheConfiguration struct {
Region string `mapstructure:"region" yaml:"region,omitempty"`
BucketName string `mapstructure:"bucketname" yaml:"bucketname,omitempty"`
}
func (s *S3Cache) Configure(cacheInfo CacheProvider) error {
if cacheInfo.S3.BucketName == "" {
log.Fatal("Bucket name not configured")
}
if cacheInfo.S3.Region == "" {
log.Fatal("Region not configured")
}
s.bucketName = cacheInfo.S3.BucketName
sess := session.Must(session.NewSessionWithOptions(session.Options{
SharedConfigState: session.SharedConfigEnable,
Config: aws.Config{
Region: aws.String(cacheInfo.S3.Region),
},
}))
s3Client := s3.New(sess)
// Check if the bucket exists, if not create it
_, err := s3Client.HeadBucket(&s3.HeadBucketInput{
Bucket: aws.String(cacheInfo.S3.BucketName),
})
if err != nil {
_, err = s3Client.CreateBucket(&s3.CreateBucketInput{
Bucket: aws.String(cacheInfo.S3.BucketName),
})
if err != nil {
return err
}
}
s.session = s3Client
return nil
}
func (s *S3Cache) Store(key string, data string) error {
// Store the object as a new file in the bucket with data as the content
_, err := s.session.PutObject(&s3.PutObjectInput{
@@ -28,6 +66,18 @@ func (s *S3Cache) Store(key string, data string) error {
}
func (s *S3Cache) Remove(key string) error {
_, err := s.session.DeleteObject(&s3.DeleteObjectInput{
Bucket: &s.bucketName,
Key: aws.String(key),
})
if err != nil {
return err
}
return nil
}
func (s *S3Cache) Load(key string) (string, error) {
// Retrieve the object from the bucket and load it into a string
@@ -40,12 +90,12 @@ func (s *S3Cache) Load(key string) (string, error) {
}
buf := new(bytes.Buffer)
buf.ReadFrom(result.Body)
_, err_read := buf.ReadFrom(result.Body)
result.Body.Close()
return buf.String(), nil
return buf.String(), err_read
}
func (s *S3Cache) List() ([]string, error) {
func (s *S3Cache) List() ([]CacheObjectDetails, error) {
// List the files in the bucket
result, err := s.session.ListObjectsV2(&s3.ListObjectsV2Input{Bucket: aws.String(s.bucketName)})
@@ -53,9 +103,12 @@ func (s *S3Cache) List() ([]string, error) {
return nil, err
}
var keys []string
var keys []CacheObjectDetails
for _, item := range result.Contents {
keys = append(keys, *item.Key)
keys = append(keys, CacheObjectDetails{
Name: *item.Key,
UpdatedAt: *item.LastModified,
})
}
return keys, nil
@@ -75,42 +128,10 @@ func (s *S3Cache) IsCacheDisabled() bool {
return s.noCache
}
func NewS3Cache(nocache bool) ICache {
var cache CacheProvider
err := viper.UnmarshalKey("cache", &cache)
if err != nil {
log.Fatal(err)
}
if cache.BucketName == "" {
log.Fatal("Bucket name not configured")
}
if cache.Region == "" {
log.Fatal("Region not configured")
}
sess := session.Must(session.NewSessionWithOptions(session.Options{
SharedConfigState: session.SharedConfigEnable,
Config: aws.Config{
Region: aws.String(cache.Region),
},
}))
s := s3.New(sess)
// Check if the bucket exists, if not create it
_, err = s.HeadBucket(&s3.HeadBucketInput{
Bucket: aws.String(cache.BucketName),
})
if err != nil {
_, _ = s.CreateBucket(&s3.CreateBucketInput{
Bucket: aws.String(cache.BucketName),
})
}
return &S3Cache{
noCache: nocache,
session: s,
bucketName: cache.BucketName,
}
func (s *S3Cache) GetName() string {
return "s3"
}
func (s *S3Cache) DisableCache() {
s.noCache = true
}

14
pkg/cache/types.go vendored Normal file
View File

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

View File

@@ -26,6 +26,7 @@ import (
v1 "k8s.io/api/core/v1"
networkv1 "k8s.io/api/networking/v1"
policyv1 "k8s.io/api/policy/v1"
gtwapi "sigs.k8s.io/gateway-api/apis/v1"
)
type IAnalyzer interface {
@@ -57,6 +58,9 @@ type PreAnalysis struct {
Node v1.Node
ValidatingWebhook regv1.ValidatingWebhookConfiguration
MutatingWebhook regv1.MutatingWebhookConfiguration
GatewayClass gtwapi.GatewayClass
Gateway gtwapi.Gateway
HTTPRoute gtwapi.HTTPRoute
// Integrations
TrivyVulnerabilityReport trivy.VulnerabilityReport
TrivyConfigAuditReport trivy.ConfigAuditReport

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,12 +15,12 @@ package trivy
import (
"fmt"
ctrl "sigs.k8s.io/controller-runtime/pkg/client"
"strings"
"github.com/aquasecurity/trivy-operator/pkg/apis/aquasecurity/v1alpha1"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
"k8s.io/client-go/rest"
)
type TrivyAnalyzer struct {
@@ -32,18 +32,9 @@ func (TrivyAnalyzer) analyzeVulnerabilityReports(a common.Analyzer) ([]common.Re
// Get all trivy VulnerabilityReports
result := &v1alpha1.VulnerabilityReportList{}
config := a.Client.GetConfig()
// Add group version to sceheme
config.ContentConfig.GroupVersion = &v1alpha1.SchemeGroupVersion
config.UserAgent = rest.DefaultKubernetesUserAgent()
config.APIPath = "/apis"
restClient, err := rest.UnversionedRESTClientFor(config)
if err != nil {
return nil, err
}
err = restClient.Get().Resource("vulnerabilityreports").Namespace(a.Namespace).Do(a.Context).Into(result)
if err != nil {
client := a.Client.CtrlClient
v1alpha1.AddToScheme(client.Scheme())
if err := client.List(a.Context, result, &ctrl.ListOptions{}); err != nil {
return nil, err
}
@@ -93,18 +84,9 @@ func (t TrivyAnalyzer) analyzeConfigAuditReports(a common.Analyzer) ([]common.Re
// Get all trivy ConfigAuditReports
result := &v1alpha1.ConfigAuditReportList{}
config := a.Client.GetConfig()
// Add group version to sceheme
config.ContentConfig.GroupVersion = &v1alpha1.SchemeGroupVersion
config.UserAgent = rest.DefaultKubernetesUserAgent()
config.APIPath = "/apis"
restClient, err := rest.UnversionedRESTClientFor(config)
if err != nil {
return nil, err
}
err = restClient.Get().Resource("configauditreports").Namespace(a.Namespace).Do(a.Context).Into(result)
if err != nil {
client := a.Client.CtrlClient
v1alpha1.AddToScheme(client.Scheme())
if err := client.List(a.Context, result, &ctrl.ListOptions{}); err != nil {
return nil, err
}

View File

@@ -16,26 +16,39 @@ package trivy
import (
"context"
"fmt"
"os"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
helmclient "github.com/mittwald/go-helm-client"
"github.com/spf13/viper"
"helm.sh/helm/v3/pkg/repo"
)
const (
Repo = "https://aquasecurity.github.io/helm-charts/"
Version = "0.13.0"
ChartName = "trivy-operator"
RepoShortName = "aqua"
ReleaseName = "trivy-operator-k8sgpt"
var (
Repo = getEnv("TRIVY_REPO", "https://aquasecurity.github.io/helm-charts/")
Version = getEnv("TRIVY_VERSION", "0.13.0")
ChartName = getEnv("TRIVY_CHART_NAME", "trivy-operator")
RepoShortName = getEnv("TRIVY_REPO_SHORT_NAME", "aqua")
ReleaseName = getEnv("TRIVY_RELEASE_NAME", "trivy-operator-k8sgpt")
)
type Trivy struct {
helm helmclient.Client
}
func getEnv(key, defaultValue string) string {
value := os.Getenv(key)
if value == "" {
return defaultValue
}
return value
}
func NewTrivy() *Trivy {
helmClient, err := helmclient.New(&helmclient.Options{})
if err != nil {
@@ -126,13 +139,52 @@ func (t *Trivy) UnDeploy(namespace string) error {
return nil
}
func (t *Trivy) IsActivate() bool {
if _, err := t.helm.GetRelease(ReleaseName); err != nil {
return false
func (t *Trivy) isDeployed() bool {
// check if aquasec apigroup is available as a marker if trivy is installed on the cluster
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)
}
return true
for _, group := range groups {
if group.Name == "aquasecurity.github.io" {
return true
}
}
return false
}
func (t *Trivy) isFilterActive() bool {
activeFilters := viper.GetStringSlice("active_filters")
for _, filter := range t.GetAnalyzerName() {
for _, af := range activeFilters {
if af == filter {
return true
}
}
}
return false
}
func (t *Trivy) IsActivate() bool {
if t.isFilterActive() && t.isDeployed() {
return true
} else {
return false
}
}
func (t *Trivy) AddAnalyzer(mergedMap *map[string]common.IAnalyzer) {

View File

@@ -14,12 +14,11 @@ limitations under the License.
package kubernetes
import (
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/client-go/kubernetes"
_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/kubectl/pkg/scheme"
ctrl "sigs.k8s.io/controller-runtime/pkg/client"
)
func (c *Client) GetConfig() *rest.Config {
@@ -30,8 +29,8 @@ func (c *Client) GetClient() kubernetes.Interface {
return c.Client
}
func (c *Client) GetRestClient() rest.Interface {
return c.RestClient
func (c *Client) GetCtrlClient() ctrl.Client {
return c.CtrlClient
}
func NewClient(kubecontext string, kubeconfig string) (*Client, error) {
@@ -59,11 +58,8 @@ func NewClient(kubecontext string, kubeconfig string) (*Client, error) {
if err != nil {
return nil, err
}
config.APIPath = "/api"
config.GroupVersion = &scheme.Scheme.PrioritizedVersionsForGroup("")[0]
config.NegotiatedSerializer = serializer.WithoutConversionCodecFactory{CodecFactory: scheme.Codecs}
restClient, err := rest.RESTClientFor(config)
ctrlClient, err := ctrl.New(config, ctrl.Options{})
if err != nil {
return nil, err
}
@@ -75,7 +71,7 @@ func NewClient(kubecontext string, kubeconfig string) (*Client, error) {
return &Client{
Client: clientSet,
RestClient: restClient,
CtrlClient: ctrlClient,
Config: config,
ServerVersion: serverVersion,
}, nil

View File

@@ -6,11 +6,12 @@ import (
"k8s.io/apimachinery/pkg/version"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime/pkg/client"
)
type Client struct {
Client kubernetes.Interface
RestClient rest.Interface
CtrlClient ctrl.Client
Config *rest.Config
ServerVersion *version.Info
}

View File

@@ -33,10 +33,14 @@ func (h *handler) Analyze(ctx context.Context, i *schemav1.AnalyzeRequest) (
i.Explain,
int(i.MaxConcurrency),
false, // Kubernetes Doc disabled in server mode
false, // Interactive mode disabled in server mode
)
config.Context = ctx // Replace context for correct timeouts.
if err != nil {
return &schemav1.AnalyzeResponse{}, err
}
defer config.Close()
config.RunAnalysis()
if i.Explain {

View File

@@ -18,17 +18,28 @@ func (h *handler) AddConfig(ctx context.Context, i *schemav1.AddConfigRequest) (
}
if i.Cache != nil {
// We check if we have a mixed cache configuration
CacheConfigured := (i.Cache.Region == "" && i.Cache.BucketName == "") || (i.Cache.ContainerName == "" && i.Cache.StorageAccount == "")
if !CacheConfigured {
return resp, status.Error(codes.InvalidArgument, "mixed cache arguments")
var err error
var remoteCache cache.CacheProvider
switch i.Cache.GetCacheType().(type) {
case *schemav1.Cache_AzureCache:
remoteCache, err = cache.NewCacheProvider("azure", "", "", i.Cache.GetAzureCache().StorageAccount, i.Cache.GetAzureCache().ContainerName, "")
case *schemav1.Cache_S3Cache:
remoteCache, err = cache.NewCacheProvider("s3", i.Cache.GetS3Cache().BucketName, i.Cache.GetS3Cache().Region, "", "", "")
case *schemav1.Cache_GcsCache:
remoteCache, err = cache.NewCacheProvider("gcs", i.Cache.GetGcsCache().BucketName, i.Cache.GetGcsCache().Region, "", "", i.Cache.GetGcsCache().GetProjectId())
default:
return resp, status.Error(codes.InvalidArgument, "Invalid cache configuration")
}
cacheProvider := cache.NewCacheProvider(i.Cache.BucketName, i.Cache.Region, i.Cache.StorageAccount, i.Cache.ContainerName)
err := cache.AddRemoteCache(cacheProvider)
if err != nil {
return resp, err
}
err = cache.AddRemoteCache(remoteCache)
if err != nil {
return resp, err
}
}
return resp, nil
}

View File

@@ -14,9 +14,11 @@ limitations under the License.
package server
import (
"context"
json "encoding/json"
"errors"
"fmt"
"log"
"net"
"net/http"
"strconv"
@@ -24,10 +26,14 @@ import (
"time"
rpc "buf.build/gen/go/k8sgpt-ai/k8sgpt/grpc/go/schema/v1/schemav1grpc"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.uber.org/zap"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/reflection"
)
@@ -42,6 +48,8 @@ type Config struct {
Handler *handler
Logger *zap.Logger
metricsServer *http.Server
listener net.Listener
EnableHttp bool
}
type Health struct {
@@ -56,8 +64,23 @@ var health = Health{
Failure: 0,
}
func (s *Config) Serve() error {
func (s *Config) Shutdown() error {
return s.listener.Close()
}
// grpcHandlerFunc returns an http.Handler that delegates to grpcServer on incoming gRPC
// connections or otherHandler otherwise.
func grpcHandlerFunc(grpcServer *grpc.Server, otherHandler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
grpcServer.ServeHTTP(w, r)
} else {
otherHandler.ServeHTTP(w, r)
}
})
}
func (s *Config) Serve() error {
var lis net.Listener
var err error
address := fmt.Sprintf(":%s", s.Port)
@@ -65,15 +88,36 @@ func (s *Config) Serve() error {
if err != nil {
return err
}
s.listener = lis
s.Logger.Info(fmt.Sprintf("binding api to %s", s.Port))
grpcServerUnaryInterceptor := grpc.UnaryInterceptor(logInterceptor(s.Logger))
grpcServer := grpc.NewServer(grpcServerUnaryInterceptor)
reflection.Register(grpcServer)
rpc.RegisterServerServiceServer(grpcServer, s.Handler)
if err := grpcServer.Serve(
lis,
); err != nil && !errors.Is(err, http.ErrServerClosed) {
return err
if s.EnableHttp {
s.Logger.Info("enabling rest/http api")
gwmux := runtime.NewServeMux()
err = rpc.RegisterServerServiceHandlerFromEndpoint(context.Background(), gwmux, fmt.Sprintf("localhost:%s", s.Port), []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())})
if err != nil {
log.Fatalln("Failed to register gateway:", err)
}
srv := &http.Server{
Addr: address,
Handler: h2c.NewHandler(grpcHandlerFunc(grpcServer, gwmux), &http2.Server{}),
}
if err := srv.Serve(lis); err != nil {
return err
}
} else {
if err := grpcServer.Serve(
lis,
); err != nil && !errors.Is(err, http.ErrServerClosed) {
return err
}
}
return nil

42
pkg/server/server_test.go Normal file
View File

@@ -0,0 +1,42 @@
package server
import (
"os"
"sync"
"testing"
"github.com/fatih/color"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
)
func TestServerInit(t *testing.T) {
logger, err := zap.NewDevelopment()
if err != nil {
color.Red("failed to create logger: %v", err)
os.Exit(1)
}
defer logger.Sync()
server_config := Config{
Backend: "openai",
Port: "0",
MetricsPort: "0",
Token: "none",
Logger: logger,
}
var wg sync.WaitGroup
go func() {
wg.Add(1)
err := server_config.Serve()
if err != nil {
assert.Fail(t, "serve: %s", err.Error())
}
server_config.Shutdown()
if err != nil {
assert.Fail(t, "shutdown: %s", err.Error())
}
wg.Done()
}()
wg.Wait()
}

View File

@@ -154,7 +154,10 @@ func SliceDiff(source, dest []string) []string {
func MaskString(input string) string {
key := make([]byte, len(input))
result := make([]rune, len(input))
rand.Read(key)
_, err := rand.Read(key)
if err != nil {
panic(err)
}
for i := range result {
result[i] = anonymizePattern[int(key[i])%len(anonymizePattern)]
}
@@ -219,3 +222,14 @@ func MapToString(m map[string]string) string {
}
return result[:len(result)-1]
}
func LabelsIncludeAny(predefinedSelector, Labels map[string]string) bool {
// Check if any label in the predefinedSelector exists in Labels
for key := range predefinedSelector {
if _, exists := Labels[key]; exists {
return true
}
}
return false
}

View File

@@ -4,13 +4,15 @@
"config:base",
"helpers:pinGitHubActionDigests",
":gitSignOff"
],
"addLabels": ["dependencies"],
"postUpdateOptions": [
"gomodTidy",
"gomodMassage"
],
"automerge": true,
"automergeType": "pr",
"platformAutomerge": true,
"packageRules": [
{
"description": "Exclude retracted cohere-go versions: https://github.com/renovatebot/renovate/issues/13012",