mirror of
https://github.com/k8sgpt-ai/k8sgpt.git
synced 2026-03-19 19:42:38 +00:00
Compare commits
8 Commits
feat/resou
...
v0.2.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64f359c428 | ||
|
|
1acb22efdb | ||
|
|
8615ea28ed | ||
|
|
a7cff482a8 | ||
|
|
6e7c583aec | ||
|
|
9121a983e5 | ||
|
|
9642202ed1 | ||
|
|
dee435514d |
@@ -1 +1 @@
|
||||
{".":"0.2.2"}
|
||||
{".":"0.2.3"}
|
||||
28
CHANGELOG.md
28
CHANGELOG.md
@@ -1,5 +1,33 @@
|
||||
# Changelog
|
||||
|
||||
## [0.2.3](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.2.2...v0.2.3) (2023-04-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add node analyzer ([#272](https://github.com/k8sgpt-ai/k8sgpt/issues/272)) ([6247a1c](https://github.com/k8sgpt-ai/k8sgpt/commit/6247a1c0f3c2ead6a59661afed06973c29e57eca))
|
||||
* add output query param on serve mode & refactor output logic ([9642202](https://github.com/k8sgpt-ai/k8sgpt/commit/9642202ed1b09c06a687651b7818c2a4df8a0c06))
|
||||
* add server metrics ([#273](https://github.com/k8sgpt-ai/k8sgpt/issues/273)) ([a3becc9](https://github.com/k8sgpt-ai/k8sgpt/commit/a3becc9906515d0567808fee9a4e322451d6dc3f))
|
||||
* envs to initialise server ([0071e25](https://github.com/k8sgpt-ai/k8sgpt/commit/0071e25992fc86c3882c2066873a2b04b43fe476))
|
||||
* rename server/main.go to server/server.go ([9121a98](https://github.com/k8sgpt-ai/k8sgpt/commit/9121a983e52fa15c07bcc3bb361df97b8085c24c))
|
||||
* running in cluster ([842f08c](https://github.com/k8sgpt-ai/k8sgpt/commit/842f08c655fde66b6b628192490e50be2ac3dcef))
|
||||
* running in cluster ([3988eb2](https://github.com/k8sgpt-ai/k8sgpt/commit/3988eb2fd0a7d29ffa7b7bbc59960ca91e50466e))
|
||||
* switch config file to XDG conform location ([dee4355](https://github.com/k8sgpt-ai/k8sgpt/commit/dee435514d7f717e4eb63b15a9d9fdb0722330ac))
|
||||
* wip blocked until we have envs ([fe2c08c](https://github.com/k8sgpt-ai/k8sgpt/commit/fe2c08cf72a6ca271d1b431be66653f1396f304d))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add new line after version cmd output ([92e7b3d](https://github.com/k8sgpt-ai/k8sgpt/commit/92e7b3d3fb00c33ac48230caac34f45729e2f6b2))
|
||||
* **deps:** update module github.com/sashabaranov/go-openai to v1.8.0 ([#277](https://github.com/k8sgpt-ai/k8sgpt/issues/277)) ([51b1b35](https://github.com/k8sgpt-ai/k8sgpt/commit/51b1b352acd24ebdc4cf9d9121f25c90e8f76ba7))
|
||||
* resolve issue with duplicated integration filters. ([960ba56](https://github.com/k8sgpt-ai/k8sgpt/commit/960ba568d0dcc2ace722dc5c9b7c846366a98070))
|
||||
* use the aiProvider object when launching the server instead of the deprecated configuration keys ([e7076ed](https://github.com/k8sgpt-ai/k8sgpt/commit/e7076ed6093aa9609d8c884b7a03e295057aaa8e))
|
||||
|
||||
|
||||
### Other
|
||||
|
||||
* updated ([f0a0c9a](https://github.com/k8sgpt-ai/k8sgpt/commit/f0a0c9aebf627d65b0192ba3d0786cefd81e1fef))
|
||||
|
||||
## [0.2.2](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.2.1...v0.2.2) (2023-04-14)
|
||||
|
||||
|
||||
|
||||
40
README.md
40
README.md
@@ -28,16 +28,16 @@ brew install k8sgpt
|
||||
**32 bit:**
|
||||
<!---x-release-please-start-version-->
|
||||
```
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.2.2/k8sgpt_386.rpm
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.2.3/k8sgpt_386.rpm
|
||||
sudo rpm -ivh k8sgpt_386.rpm
|
||||
```
|
||||
<!---x-release-please-end-->
|
||||
|
||||
**64 bit:**
|
||||
|
||||
|
||||
<!---x-release-please-start-version-->
|
||||
```
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.2.2/k8sgpt_amd64.rpm
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.2.3/k8sgpt_amd64.rpm
|
||||
sudo rpm -ivh -i k8sgpt_amd64.rpm
|
||||
```
|
||||
<!---x-release-please-end-->
|
||||
@@ -49,15 +49,15 @@ brew install k8sgpt
|
||||
**32 bit:**
|
||||
<!---x-release-please-start-version-->
|
||||
```
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.2.2/k8sgpt_386.deb
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.2.3/k8sgpt_386.deb
|
||||
sudo dpkg -i k8sgpt_386.deb
|
||||
```
|
||||
<!---x-release-please-end-->
|
||||
**64 bit:**
|
||||
|
||||
|
||||
<!---x-release-please-start-version-->
|
||||
```
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.2.2/k8sgpt_amd64.deb
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.2.3/k8sgpt_amd64.deb
|
||||
sudo dpkg -i k8sgpt_amd64.deb
|
||||
```
|
||||
<!---x-release-please-end-->
|
||||
@@ -70,14 +70,14 @@ brew install k8sgpt
|
||||
**32 bit:**
|
||||
<!---x-release-please-start-version-->
|
||||
```
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.2.2/k8sgpt_386.apk
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.2.3/k8sgpt_386.apk
|
||||
apk add k8sgpt_386.apk
|
||||
```
|
||||
<!---x-release-please-end-->
|
||||
**64 bit:**
|
||||
<!---x-release-please-start-version-->
|
||||
```
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.2.2/k8sgpt_amd64.apk
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.2.3/k8sgpt_amd64.apk
|
||||
apk add k8sgpt_amd64.apk
|
||||
```
|
||||
<!---x-release-please-end-->x
|
||||
@@ -88,7 +88,7 @@ brew install k8sgpt
|
||||
When installing Homebrew on WSL or Linux, you may encounter the following error:
|
||||
|
||||
```
|
||||
==> Installing k8sgpt from k8sgpt-ai/k8sgpt Error: The following formula cannot be installed from a bottle and must be
|
||||
==> Installing k8sgpt from k8sgpt-ai/k8sgpt Error: The following formula cannot be installed from a bottle and must be
|
||||
built from the source. k8sgpt Install Clang or run brew install gcc.
|
||||
```
|
||||
|
||||
@@ -102,7 +102,7 @@ If you install gcc as suggested, the problem will persist. Therefore, you need t
|
||||
|
||||
## Windows
|
||||
|
||||
* Download the latest Windows binaries of **k8sgpt** from the [Release](https://github.com/k8sgpt-ai/k8sgpt/releases)
|
||||
* Download the latest Windows binaries of **k8sgpt** from the [Release](https://github.com/k8sgpt-ai/k8sgpt/releases)
|
||||
tab based on your system architecture.
|
||||
* Extract the downloaded package to your desired location. Configure the system *path* variable with the binary location
|
||||
|
||||
@@ -117,7 +117,7 @@ If you install gcc as suggested, the problem will persist. Therefore, you need t
|
||||
|
||||
* Currently the default AI provider is OpenAI, you will need to generate an API key from [OpenAI](https://openai.com)
|
||||
* You can do this by running `k8sgpt generate` to open a browser link to generate it
|
||||
* Run `k8sgpt auth` to set it in k8sgpt.
|
||||
* Run `k8sgpt auth` 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.
|
||||
@@ -127,7 +127,7 @@ If you install gcc as suggested, the problem will persist. Therefore, you need t
|
||||
|
||||
## Analyzers
|
||||
|
||||
K8sGPT uses analyzers to triage and diagnose issues in your cluster. It has a set of analyzers that are built in, but
|
||||
K8sGPT uses analyzers to triage and diagnose issues in your cluster. It has a set of analyzers that are built in, but
|
||||
you will be able to write your own analyzers.
|
||||
|
||||
### Built in analyzers
|
||||
@@ -275,17 +275,25 @@ The Kubernetes system is trying to scale a StatefulSet named fake-deployment usi
|
||||
|
||||
## What about kubectl-ai?
|
||||
|
||||
The kubectl-ai [project](https://github.com/sozercan/kubectl-ai) uses AI to create manifests and apply them to the
|
||||
The kubectl-ai [project](https://github.com/sozercan/kubectl-ai) uses AI to create manifests and apply them to the
|
||||
cluster. It is not what we are trying to do here, it is focusing on writing YAML manifests.
|
||||
|
||||
K8sgpt is focused on triaging and diagnosing issues in your cluster. It is a tool for SRE, Platform & DevOps engineers
|
||||
to help them understand what is going on in their cluster. Cutting through the noise of logs and multiple tools to find
|
||||
K8sgpt is focused on triaging and diagnosing issues in your cluster. It is a tool for SRE, Platform & DevOps engineers
|
||||
to help them understand what is going on in their cluster. Cutting through the noise of logs and multiple tools to find
|
||||
the root cause of an issue.
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
`k8sgpt` stores config data in `~/.k8sgpt.yaml` the data is stored in plain text, including your OpenAI key.
|
||||
`k8sgpt` stores config data in the `$XDG_CONFIG_HOME/k8sgpt/k8sgpt.yaml` file. The data is stored in plain text, including your OpenAI key.
|
||||
|
||||
Config file locations:
|
||||
| OS | Path |
|
||||
|---------|--------------------------------------------------|
|
||||
| MacOS | ~/Library/Application Support/k8sgpt/k8sgpt.yaml |
|
||||
| Linux | ~/.config/k8sgpt/k8sgpt.yaml |
|
||||
| Windows | %LOCALAPPDATA%/k8sgpt/k8sgpt.yaml |
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
@@ -51,17 +51,12 @@ var AnalyzeCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
// print results
|
||||
switch output {
|
||||
case "json":
|
||||
output, err := config.JsonOutput()
|
||||
if err != nil {
|
||||
color.Red("Error: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(string(output))
|
||||
default:
|
||||
config.PrintOutput()
|
||||
output, err := config.PrintOutput(output)
|
||||
if err != nil {
|
||||
color.Red("Error: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(string(output))
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
59
cmd/root.go
59
cmd/root.go
@@ -1,11 +1,13 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/adrg/xdg"
|
||||
"github.com/fatih/color"
|
||||
"github.com/k8sgpt-ai/k8sgpt/cmd/serve"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||
"github.com/k8sgpt-ai/k8sgpt/cmd/analyze"
|
||||
"github.com/k8sgpt-ai/k8sgpt/cmd/auth"
|
||||
"github.com/k8sgpt-ai/k8sgpt/cmd/filters"
|
||||
@@ -44,6 +46,8 @@ func Execute(v string) {
|
||||
}
|
||||
|
||||
func init() {
|
||||
performConfigMigrationIfNeeded()
|
||||
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
var kubeconfigPath string
|
||||
@@ -67,14 +71,12 @@ func initConfig() {
|
||||
// Use config file from the flag.
|
||||
viper.SetConfigFile(cfgFile)
|
||||
} else {
|
||||
// Find home directory.
|
||||
home, err := os.UserHomeDir()
|
||||
cobra.CheckErr(err)
|
||||
// the config will belocated under `~/.config/k8sgpt/k8sgpt.yaml` on linux
|
||||
configDir := filepath.Join(xdg.ConfigHome, "k8sgpt")
|
||||
|
||||
// Search config in home directory with name ".k8sgpt.git" (without extension).
|
||||
viper.AddConfigPath(home)
|
||||
viper.AddConfigPath(configDir)
|
||||
viper.SetConfigType("yaml")
|
||||
viper.SetConfigName(".k8sgpt")
|
||||
viper.SetConfigName("k8sgpt")
|
||||
|
||||
viper.SafeWriteConfig()
|
||||
}
|
||||
@@ -90,3 +92,44 @@ func initConfig() {
|
||||
// fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
|
||||
}
|
||||
}
|
||||
|
||||
func performConfigMigrationIfNeeded() {
|
||||
oldConfig, err := getLegacyConfigFilePath()
|
||||
cobra.CheckErr(err)
|
||||
oldConfigExists, err := util.FileExists(oldConfig)
|
||||
cobra.CheckErr(err)
|
||||
|
||||
newConfig := getConfigFilePath()
|
||||
newConfigExists, err := util.FileExists(newConfig)
|
||||
cobra.CheckErr(err)
|
||||
|
||||
configDir := filepath.Dir(newConfig)
|
||||
err = util.EnsureDirExists(configDir)
|
||||
cobra.CheckErr(err)
|
||||
|
||||
if oldConfigExists && newConfigExists {
|
||||
fmt.Fprintln(os.Stderr, color.RedString("Warning: Legacy config file at `%s` detected! This file will be ignored!", oldConfig))
|
||||
return
|
||||
}
|
||||
|
||||
if oldConfigExists && !newConfigExists {
|
||||
fmt.Fprintln(os.Stderr, color.RedString("Performing config file migration from `%s` to `%s`", oldConfig, newConfig))
|
||||
|
||||
err = os.Rename(oldConfig, newConfig)
|
||||
cobra.CheckErr(err)
|
||||
}
|
||||
}
|
||||
|
||||
func getConfigFilePath() string {
|
||||
return filepath.Join(xdg.ConfigHome, "k8sgpt", "k8sgpt.yaml")
|
||||
}
|
||||
|
||||
func getLegacyConfigFilePath() (string, error) {
|
||||
home, err := os.UserHomeDir()
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return filepath.Join(home, ".k8sgpt.yaml"), nil
|
||||
}
|
||||
|
||||
@@ -19,17 +19,17 @@ spec:
|
||||
containers:
|
||||
- name: k8sgpt-container
|
||||
imagePullPolicy: Always
|
||||
image: ghcr.io/k8sgpt-ai/k8sgpt:dev-202304151719 #x-release-please-version
|
||||
image: ghcr.io/k8sgpt-ai/k8sgpt:v0.2.3 #x-release-please-version
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
args: ["serve"]
|
||||
resources:
|
||||
limits:
|
||||
cpu: "1"
|
||||
memory: "512Mi"
|
||||
requests:
|
||||
cpu: "0.5"
|
||||
memory: "256Mi"
|
||||
requests:
|
||||
cpu: "0.125"
|
||||
memory: "64Mi"
|
||||
env:
|
||||
- name: K8SGPT_MODEL
|
||||
value: "gpt-3.5-turbo"
|
||||
|
||||
2
go.mod
2
go.mod
@@ -21,6 +21,8 @@ require (
|
||||
|
||||
)
|
||||
|
||||
require github.com/adrg/xdg v0.4.0 // indirect
|
||||
|
||||
require (
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
|
||||
3
go.sum
3
go.sum
@@ -69,6 +69,8 @@ github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 h1:ra2OtmuW0A
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
|
||||
github.com/a8m/expect v1.0.0/go.mod h1:4IwSCMumY49ScypDnjNbYEjgVeqy1/U2cEs3Lat96eA=
|
||||
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
|
||||
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
|
||||
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
|
||||
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
|
||||
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
@@ -946,6 +948,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
||||
@@ -2,7 +2,6 @@ package analysis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
@@ -149,45 +148,6 @@ func (a *Analysis) RunAnalysis() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Analysis) JsonOutput() ([]byte, error) {
|
||||
var problems int
|
||||
var status AnalysisStatus
|
||||
for _, result := range a.Results {
|
||||
problems += len(result.Error)
|
||||
}
|
||||
if problems > 0 {
|
||||
status = StateProblemDetected
|
||||
} else {
|
||||
status = StateOK
|
||||
}
|
||||
|
||||
result := JsonOutput{
|
||||
Problems: problems,
|
||||
Results: a.Results,
|
||||
Status: status,
|
||||
}
|
||||
output, err := json.MarshalIndent(result, "", " ")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error marshalling json: %v", err)
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func (a *Analysis) PrintOutput() {
|
||||
fmt.Println("")
|
||||
if len(a.Results) == 0 {
|
||||
fmt.Println(color.GreenString("No problems detected"))
|
||||
}
|
||||
for n, result := range a.Results {
|
||||
fmt.Printf("%s %s(%s)\n", color.CyanString("%d", n),
|
||||
color.YellowString(result.Name), color.CyanString(result.ParentObject))
|
||||
for _, err := range result.Error {
|
||||
fmt.Printf("- %s %s\n", color.RedString("Error:"), color.RedString(err.Text))
|
||||
}
|
||||
fmt.Println(color.GreenString(result.Details + "\n"))
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Analysis) GetAIResults(output string, anonymize bool) error {
|
||||
if len(a.Results) == 0 {
|
||||
return nil
|
||||
|
||||
@@ -22,7 +22,7 @@ func TestAnalysis_NoProblemJsonOutput(t *testing.T) {
|
||||
Results: []common.Result{},
|
||||
}
|
||||
|
||||
gotJson, err := analysis.JsonOutput()
|
||||
gotJson, err := analysis.PrintOutput("json")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -75,7 +75,7 @@ func TestAnalysis_ProblemJsonOutput(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
gotJson, err := analysis.JsonOutput()
|
||||
gotJson, err := analysis.PrintOutput("json")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -136,7 +136,7 @@ func TestAnalysis_MultipleProblemJsonOutput(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
gotJson, err := analysis.JsonOutput()
|
||||
gotJson, err := analysis.PrintOutput("json")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
72
pkg/analysis/output.go
Normal file
72
pkg/analysis/output.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package analysis
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
var outputFormats = map[string]func(*Analysis) ([]byte, error){
|
||||
"json": (*Analysis).jsonOutput,
|
||||
"text": (*Analysis).textOutput,
|
||||
}
|
||||
|
||||
func getOutputFormats() []string {
|
||||
formats := make([]string, 0, len(outputFormats))
|
||||
for format := range outputFormats {
|
||||
formats = append(formats, format)
|
||||
}
|
||||
return formats
|
||||
}
|
||||
|
||||
func (a *Analysis) PrintOutput(format string) ([]byte, error) {
|
||||
outputFunc, ok := outputFormats[format]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unsupported output format: %s. Available format %s", format, strings.Join(getOutputFormats(), ","))
|
||||
}
|
||||
return outputFunc(a)
|
||||
}
|
||||
|
||||
func (a *Analysis) jsonOutput() ([]byte, error) {
|
||||
var problems int
|
||||
var status AnalysisStatus
|
||||
for _, result := range a.Results {
|
||||
problems += len(result.Error)
|
||||
}
|
||||
if problems > 0 {
|
||||
status = StateProblemDetected
|
||||
} else {
|
||||
status = StateOK
|
||||
}
|
||||
|
||||
result := JsonOutput{
|
||||
Problems: problems,
|
||||
Results: a.Results,
|
||||
Status: status,
|
||||
}
|
||||
output, err := json.MarshalIndent(result, "", " ")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error marshalling json: %v", err)
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func (a *Analysis) textOutput() ([]byte, error) {
|
||||
var output strings.Builder
|
||||
output.WriteString("\n")
|
||||
if len(a.Results) == 0 {
|
||||
output.WriteString(color.GreenString("No problems detected\n"))
|
||||
return []byte(output.String()), nil
|
||||
}
|
||||
for n, result := range a.Results {
|
||||
output.WriteString(fmt.Sprintf("%s %s(%s)\n", color.CyanString("%d", n),
|
||||
color.YellowString(result.Name), color.CyanString(result.ParentObject)))
|
||||
for _, err := range result.Error {
|
||||
output.WriteString(fmt.Sprintf("- %s %s\n", color.RedString("Error:"), color.RedString(err.Text)))
|
||||
}
|
||||
output.WriteString(color.GreenString(result.Details + "\n"))
|
||||
}
|
||||
return []byte(output.String()), nil
|
||||
}
|
||||
@@ -42,6 +42,11 @@ func (s *Config) analyzeHandler(w http.ResponseWriter, r *http.Request) {
|
||||
anonymize := getBoolParam(r.URL.Query().Get("anonymize"))
|
||||
nocache := getBoolParam(r.URL.Query().Get("nocache"))
|
||||
language := r.URL.Query().Get("language")
|
||||
s.Output = r.URL.Query().Get("output")
|
||||
|
||||
if s.Output == "" {
|
||||
s.Output = "json"
|
||||
}
|
||||
|
||||
config, err := analysis.NewAnalysis(s.Backend, language, []string{}, namespace, nocache, explain)
|
||||
if err != nil {
|
||||
@@ -65,14 +70,15 @@ func (s *Config) analyzeHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
output, err := config.JsonOutput()
|
||||
out, err := config.PrintOutput(s.Output)
|
||||
if err != nil {
|
||||
color.Red("Error: %v", err)
|
||||
health.Failure++
|
||||
fmt.Fprintf(w, err.Error())
|
||||
}
|
||||
|
||||
health.Success++
|
||||
fmt.Fprintf(w, string(output))
|
||||
fmt.Fprintf(w, string(out))
|
||||
}
|
||||
|
||||
func (s *Config) Serve() error {
|
||||
@@ -3,8 +3,10 @@ package util
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"regexp"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||
@@ -150,3 +152,23 @@ func GetPodListByLabels(client k.Interface,
|
||||
|
||||
return pods, nil
|
||||
}
|
||||
|
||||
func FileExists(path string) (bool, error) {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
return true, nil
|
||||
} else if errors.Is(err, os.ErrNotExist) {
|
||||
return false, nil
|
||||
} else {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
func EnsureDirExists(dir string) error {
|
||||
err := os.Mkdir(dir, 0755)
|
||||
|
||||
if errors.Is(err, os.ErrExist) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user