Skip to content

Commit

Permalink
add cache for namespaces for one hour
Browse files Browse the repository at this point in the history
Signed-off-by: Egor Kovetskiy <[email protected]>
  • Loading branch information
kovetskiy committed Jul 4, 2023
1 parent ea93cf0 commit ce4f44e
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 1 deletion.
102 changes: 102 additions & 0 deletions cmd/tubectl/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package main

import (
"encoding/json"
"os"
"path/filepath"
"time"

"github.com/reconquest/karma-go"
)

// useCache executes do() function and caches its result in path.
// if cache is older than ttl, do() function is executed again.
// if do() function returns error, cache is not updated.
func useCache[T any](
do func() (T, error),
path string,
ttl time.Duration,
) (T, error) {
var result T

userCacheDir, err := os.UserCacheDir()
if err != nil {
return result, karma.Format(
err,
"unable to get user cache dir",
)
}

cacheDir := filepath.Join(userCacheDir, "tubekit")

cachePath := filepath.Join(cacheDir, path)

cacheFile, err := os.OpenFile(cachePath, os.O_RDWR, 0644)
switch {
case err == nil:
defer cacheFile.Close()

stat, err := cacheFile.Stat()
if err != nil {
return result, karma.Format(
err,
"unable to stat cache file: %s", cachePath,
)
}

if time.Since(stat.ModTime()) < ttl {
var result T
err := json.NewDecoder(cacheFile).Decode(&result)
if err != nil {
return result, karma.Format(
err,
"unable to decode cache file: %s", cachePath,
)
}

return result, nil
}

case os.IsNotExist(err):
err = os.MkdirAll(filepath.Dir(cachePath), 0755)
if err != nil {
return result, karma.Format(
err,
"unable to create cache dir for: %s", cachePath,
)
}

cacheFile, err = os.Create(cachePath)
if err != nil {
return result, karma.Format(
err,
"unable to create cache file: %s", cachePath,
)
}

defer cacheFile.Close()

default:
return result, karma.Format(
err,
"unable to open cache file: %s", cachePath,
)
}

result, err = do()
if err != nil {
return result, err
}

encoder := json.NewEncoder(cacheFile)

err = encoder.Encode(result)
if err != nil {
return result, karma.Format(
err,
"unable to encode cache file: %s", path,
)
}

return result, nil
}
2 changes: 1 addition & 1 deletion cmd/tubectl/complete.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func completeParams(client string, params *Params) (*Params, error) {
}

if params.CompleteNamespace {
namespaces, err := requestNamespaces(client, params)
namespaces, err := requestNamespacesWithCache(client, params)
if err != nil {
return params, karma.Format(
err,
Expand Down
14 changes: 14 additions & 0 deletions cmd/tubectl/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"os/exec"
"sort"
"time"

"github.com/reconquest/karma-go"
clientcmdapi "k8s.io/client-go/tools/clientcmd"
Expand Down Expand Up @@ -44,6 +45,19 @@ func parseKubernetesContexts(kubeconfig string) ([]string, error) {
return contexts, nil
}

// requestNamespacesWithCache returns list of namespaces available in current context.
// it uses requestNamespaces() to retrieve list of namespaces from kubernetes
// but caches the result in XDG_CACHE_HOME/tubekit/{current-context}/namespaces.json
func requestNamespacesWithCache(client string, params *Params) ([]string, error) {
return useCache(
func() ([]string, error) {
return requestNamespaces(client, params)
},
fmt.Sprintf("%s/namespaces.json", params.Context),
time.Hour,
)
}

func requestNamespaces(client string, params *Params) ([]string, error) {
// omit namespace argument because requesting list of them
cmd, args := getCommand(
Expand Down

0 comments on commit ce4f44e

Please sign in to comment.