Skip to content

Commit

Permalink
feat: use OS conform path for storing cached results
Browse files Browse the repository at this point in the history
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 k8sgpt-ai#323

Signed-off-by: Patrick Pichler <git@patrickpichler.dev>
  • Loading branch information
Patrick Pichler committed Apr 24, 2023
1 parent e7f74db commit 7a3fbea
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 22 deletions.
4 changes: 3 additions & 1 deletion pkg/ai/iai.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ package ai

import (
"context"

"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
)

type IAI interface {
Configure(config IAIConfig, language 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.Cache) (string, error)
GetName() string
}

Expand Down
16 changes: 8 additions & 8 deletions pkg/ai/noopai.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import (
"strings"

"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
"github.com/spf13/viper"
)

type NoOpAIClient struct {
Expand All @@ -44,7 +44,7 @@ func (c *NoOpAIClient) GetCompletion(ctx context.Context, prompt string) (string
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.Cache) (string, error) {
// parse the text with the AI backend
inputKey := strings.Join(prompt, " ")
// Check for cached data
Expand All @@ -57,13 +57,13 @@ func (a *NoOpAIClient) Parse(ctx context.Context, prompt []string, nocache bool)
return "", err
}

if !viper.IsSet(cacheKey) {
viper.Set(cacheKey, base64.StdEncoding.EncodeToString([]byte(response)))
if err := viper.WriteConfig(); err != nil {
color.Red("error writing config: %v", err)
return "", nil
}
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
}

Expand Down
25 changes: 15 additions & 10 deletions pkg/ai/openai.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ import (
"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"
"github.com/spf13/viper"
)

type OpenAIClient struct {
Expand Down Expand Up @@ -70,15 +70,20 @@ func (c *OpenAIClient) GetCompletion(ctx context.Context, prompt string) (string
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.Cache) (string, error) {
inputKey := strings.Join(prompt, " ")
// Check for cached data
sEnc := base64.StdEncoding.EncodeToString([]byte(inputKey))
cacheKey := util.GetCacheKey(a.GetName(), a.language, sEnc)
// find in viper cache
if viper.IsSet(cacheKey) && !nocache {
if cache.Exists(cacheKey) {
// retrieve data from cache
response := viper.GetString(cacheKey)
response, err := cache.Load(cacheKey)

if err != nil {
return "", err
}

if response == "" {
color.Red("error retrieving cached data")
return "", nil
Expand All @@ -96,13 +101,13 @@ func (a *OpenAIClient) Parse(ctx context.Context, prompt []string, nocache bool)
return "", err
}

if !viper.IsSet(cacheKey) || nocache {
viper.Set(cacheKey, base64.StdEncoding.EncodeToString([]byte(response)))
if err := viper.WriteConfig(); err != nil {
color.Red("error writing config: %v", err)
return "", nil
}
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
}

Expand Down
7 changes: 4 additions & 3 deletions pkg/analysis/analysis.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
"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/kubernetes"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
Expand All @@ -38,7 +39,7 @@ type Analysis struct {
AIClient ai.IAI
Results []common.Result
Namespace string
NoCache bool
Cache cache.Cache
Explain bool
}

Expand Down Expand Up @@ -104,7 +105,7 @@ func NewAnalysis(backend string, language string, filters []string, namespace st
Client: client,
AIClient: aiClient,
Namespace: namespace,
NoCache: noCache,
Cache: cache.New(noCache),
Explain: explain,
}, nil
}
Expand Down Expand Up @@ -185,7 +186,7 @@ func (a *Analysis) GetAIResults(output string, anonymize bool) error {
}
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 {
// FIXME: can we avoid checking if output is json multiple times?
// maybe implement the progress bar better?
Expand Down
15 changes: 15 additions & 0 deletions pkg/cache/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package cache

type Cache interface {
Store(key string, data string) error
Load(key string) (string, error)
Exists(key string) bool
}

func New(noCache bool) Cache {
if noCache {
return &NoopCache{}
}

return &FileBasedCache{}
}
58 changes: 58 additions & 0 deletions pkg/cache/file_based.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package cache

import (
"fmt"
"os"
"path/filepath"

"github.com/adrg/xdg"
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
)

var _ (Cache) = (*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 changes: 17 additions & 0 deletions pkg/cache/noop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package cache

var _ (Cache) = (*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
}

0 comments on commit 7a3fbea

Please sign in to comment.