mirror of
https://github.com/k8sgpt-ai/k8sgpt.git
synced 2025-09-10 11:39:40 +00:00
feat: use OS conform path for storing cached results
Instead of storing cached values in the config yaml, they are now stored under these OS specific locations: * Linux: `~/.cache/k8sgpt` * MacOS: `~/Library/Caches` * Windows: `%LocalAppData%\cache` Additionally a `Cache` package and interface has been introduced. Currently there are two implementations: * Noop - Doesn't do anything * FileBased - Stores data in files under the locations listed above fixes #323 Signed-off-by: Patrick Pichler <git@patrickpichler.dev>
This commit is contained in:
@@ -15,12 +15,14 @@ package ai
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IAI interface {
|
type IAI interface {
|
||||||
Configure(config IAIConfig, language string) error
|
Configure(config IAIConfig, language string) error
|
||||||
GetCompletion(ctx context.Context, prompt string) (string, error)
|
GetCompletion(ctx context.Context, prompt string) (string, error)
|
||||||
Parse(ctx context.Context, prompt []string, nocache bool) (string, error)
|
Parse(ctx context.Context, prompt []string, cache cache.ICache) (string, error)
|
||||||
GetName() string
|
GetName() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -20,8 +20,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
|
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type NoOpAIClient struct {
|
type NoOpAIClient struct {
|
||||||
@@ -44,7 +44,7 @@ func (c *NoOpAIClient) GetCompletion(ctx context.Context, prompt string) (string
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *NoOpAIClient) Parse(ctx context.Context, prompt []string, nocache bool) (string, error) {
|
func (a *NoOpAIClient) Parse(ctx context.Context, prompt []string, cache cache.ICache) (string, error) {
|
||||||
// parse the text with the AI backend
|
// parse the text with the AI backend
|
||||||
inputKey := strings.Join(prompt, " ")
|
inputKey := strings.Join(prompt, " ")
|
||||||
// Check for cached data
|
// Check for cached data
|
||||||
@@ -57,13 +57,13 @@ func (a *NoOpAIClient) Parse(ctx context.Context, prompt []string, nocache bool)
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !viper.IsSet(cacheKey) {
|
err = cache.Store(cacheKey, base64.StdEncoding.EncodeToString([]byte(response)))
|
||||||
viper.Set(cacheKey, base64.StdEncoding.EncodeToString([]byte(response)))
|
|
||||||
if err := viper.WriteConfig(); err != nil {
|
if err != nil {
|
||||||
color.Red("error writing config: %v", err)
|
color.Red("error storing value to cache: %v", err)
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -20,12 +20,12 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||||
|
|
||||||
"github.com/sashabaranov/go-openai"
|
"github.com/sashabaranov/go-openai"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type OpenAIClient struct {
|
type OpenAIClient struct {
|
||||||
@@ -70,15 +70,20 @@ func (c *OpenAIClient) GetCompletion(ctx context.Context, prompt string) (string
|
|||||||
return resp.Choices[0].Message.Content, nil
|
return resp.Choices[0].Message.Content, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *OpenAIClient) Parse(ctx context.Context, prompt []string, nocache bool) (string, error) {
|
func (a *OpenAIClient) Parse(ctx context.Context, prompt []string, cache cache.ICache) (string, error) {
|
||||||
inputKey := strings.Join(prompt, " ")
|
inputKey := strings.Join(prompt, " ")
|
||||||
// Check for cached data
|
// Check for cached data
|
||||||
sEnc := base64.StdEncoding.EncodeToString([]byte(inputKey))
|
sEnc := base64.StdEncoding.EncodeToString([]byte(inputKey))
|
||||||
cacheKey := util.GetCacheKey(a.GetName(), a.language, sEnc)
|
cacheKey := util.GetCacheKey(a.GetName(), a.language, sEnc)
|
||||||
// find in viper cache
|
// find in viper cache
|
||||||
if viper.IsSet(cacheKey) && !nocache {
|
if cache.Exists(cacheKey) {
|
||||||
// retrieve data from cache
|
// retrieve data from cache
|
||||||
response := viper.GetString(cacheKey)
|
response, err := cache.Load(cacheKey)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
if response == "" {
|
if response == "" {
|
||||||
color.Red("error retrieving cached data")
|
color.Red("error retrieving cached data")
|
||||||
return "", nil
|
return "", nil
|
||||||
@@ -96,13 +101,13 @@ func (a *OpenAIClient) Parse(ctx context.Context, prompt []string, nocache bool)
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !viper.IsSet(cacheKey) || nocache {
|
err = cache.Store(cacheKey, base64.StdEncoding.EncodeToString([]byte(response)))
|
||||||
viper.Set(cacheKey, base64.StdEncoding.EncodeToString([]byte(response)))
|
|
||||||
if err := viper.WriteConfig(); err != nil {
|
if err != nil {
|
||||||
color.Red("error writing config: %v", err)
|
color.Red("error storing value to cache: %v", err)
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -25,6 +25,7 @@ import (
|
|||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer"
|
||||||
|
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/common"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||||
@@ -33,14 +34,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Analysis struct {
|
type Analysis struct {
|
||||||
Context context.Context
|
Context context.Context
|
||||||
Filters []string
|
Filters []string
|
||||||
Client *kubernetes.Client
|
Client *kubernetes.Client
|
||||||
AIClient ai.IAI
|
AIClient ai.IAI
|
||||||
Results []common.Result
|
Results []common.Result
|
||||||
Namespace string
|
Namespace string
|
||||||
NoCache bool
|
Cache cache.ICache
|
||||||
Explain bool
|
Explain bool
|
||||||
MaxConcurrency int
|
MaxConcurrency int
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,13 +102,13 @@ func NewAnalysis(backend string, language string, filters []string, namespace st
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &Analysis{
|
return &Analysis{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
Filters: filters,
|
Filters: filters,
|
||||||
Client: client,
|
Client: client,
|
||||||
AIClient: aiClient,
|
AIClient: aiClient,
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
NoCache: noCache,
|
Cache: cache.New(noCache),
|
||||||
Explain: explain,
|
Explain: explain,
|
||||||
MaxConcurrency: maxConcurrency,
|
MaxConcurrency: maxConcurrency,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -229,7 +230,7 @@ func (a *Analysis) GetAIResults(output string, anonymize bool) error {
|
|||||||
}
|
}
|
||||||
texts = append(texts, failure.Text)
|
texts = append(texts, failure.Text)
|
||||||
}
|
}
|
||||||
parsedText, err := a.AIClient.Parse(a.Context, texts, a.NoCache)
|
parsedText, err := a.AIClient.Parse(a.Context, texts, a.Cache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// FIXME: can we avoid checking if output is json multiple times?
|
// FIXME: can we avoid checking if output is json multiple times?
|
||||||
// maybe implement the progress bar better?
|
// maybe implement the progress bar better?
|
||||||
|
15
pkg/cache/cache.go
vendored
Normal file
15
pkg/cache/cache.go
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
type ICache interface {
|
||||||
|
Store(key string, data string) error
|
||||||
|
Load(key string) (string, error)
|
||||||
|
Exists(key string) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(noCache bool) ICache {
|
||||||
|
if noCache {
|
||||||
|
return &NoopCache{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &FileBasedCache{}
|
||||||
|
}
|
58
pkg/cache/file_based.go
vendored
Normal file
58
pkg/cache/file_based.go
vendored
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/adrg/xdg"
|
||||||
|
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ (ICache) = (*FileBasedCache)(nil)
|
||||||
|
|
||||||
|
type FileBasedCache struct{}
|
||||||
|
|
||||||
|
func (*FileBasedCache) Exists(key string) bool {
|
||||||
|
path, err := xdg.CacheFile(filepath.Join("k8sgpt", key))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "warning: error while testing if cache key exists:", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
exists, err := util.FileExists(path)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "warning: error while testing if cache key exists:", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*FileBasedCache) Load(key string) (string, error) {
|
||||||
|
path, err := xdg.CacheFile(filepath.Join("k8sgpt", key))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := os.ReadFile(path)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*FileBasedCache) Store(key string, data string) error {
|
||||||
|
path, err := xdg.CacheFile(filepath.Join("k8sgpt", key))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.WriteFile(path, []byte(data), 0600)
|
||||||
|
}
|
17
pkg/cache/noop.go
vendored
Normal file
17
pkg/cache/noop.go
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
var _ (ICache) = (*NoopCache)(nil)
|
||||||
|
|
||||||
|
type NoopCache struct{}
|
||||||
|
|
||||||
|
func (c *NoopCache) Store(key string, data string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NoopCache) Load(key string) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NoopCache) Exists(key string) bool {
|
||||||
|
return false
|
||||||
|
}
|
Reference in New Issue
Block a user