Skip to content

Commit

Permalink
feat: switch config file to XDG conform location
Browse files Browse the repository at this point in the history
The config file is now located in an folder according to the XDG
specification (`XDG_CONFIG_HOME`).

Migration is performed automatically.

This fixes #235.

Signed-off-by: Patrick Pichler <git@patrickpichler.dev>
  • Loading branch information
Patrick Pichler authored and Patrick Pichler committed Apr 15, 2023
1 parent 51b1b35 commit dee4355
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 17 deletions.
28 changes: 18 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ brew install k8sgpt
<!---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
Expand All @@ -54,7 +54,7 @@ brew install k8sgpt
```
<!---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
Expand Down Expand Up @@ -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.
```

Expand All @@ -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

Expand All @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down
60 changes: 53 additions & 7 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package cmd

import (
"github.com/k8sgpt-ai/k8sgpt/cmd/serve"
"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"
Expand Down Expand Up @@ -43,6 +48,8 @@ func Execute(v string) {
}

func init() {
performConfigMigrationIfNeeded()

cobra.OnInitialize(initConfig)

var kubeconfigPath string
Expand All @@ -66,14 +73,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()
}
Expand All @@ -88,3 +93,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
}
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand Down
22 changes: 22 additions & 0 deletions pkg/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package util
import (
"context"
"encoding/base64"
"errors"
"fmt"
"math/rand"
"os"
"regexp"

"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
Expand Down Expand Up @@ -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
}

0 comments on commit dee4355

Please sign in to comment.