Skip to content

Commit

Permalink
Caching the way I should have from the beginning
Browse files Browse the repository at this point in the history
  • Loading branch information
Shackelford-Arden committed Jul 12, 2024
1 parent b4dbbbf commit 0df03cc
Show file tree
Hide file tree
Showing 10 changed files with 512 additions and 15 deletions.
5 changes: 5 additions & 0 deletions .changes/unreleased/Added-20240712-115926.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
kind: Added
body: Caching, the way it should have been from the beginning.
time: 2024-07-12T11:59:26.503843-05:00
custom:
Author: Shackelford-Arden
18 changes: 16 additions & 2 deletions cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ package cache
import (
"encoding/json"
"fmt"
"os"

"github.com/Shackelford-Arden/hctx/config"
"github.com/Shackelford-Arden/hctx/models"
"os"
)

const FilePerms = os.FileMode(0600)
Expand Down Expand Up @@ -81,7 +82,8 @@ func (c *Cache) Update(stackName string, data models.StackCache) error {
return nil
}

func (c *Cache) Get(stackName string) *models.StackCache {
// GetStack retrieves the cache for the given stack.
func (c *Cache) GetStack(stackName string) *models.StackCache {
var cacheStack *models.StackCache

for name, cache := range *c {
Expand All @@ -94,6 +96,18 @@ func (c *Cache) Get(stackName string) *models.StackCache {
return cacheStack
}

// Get retrieves the full cache blob, typically for displaying.
func (c *Cache) Get() (map[string]models.StackCache, error) {

cache := map[string]models.StackCache{}

for stackName, stackCache := range *c {
cache[stackName] = stackCache
}

return cache, nil
}

func (c *Cache) Save(path string) error {

cp := path
Expand Down
4 changes: 2 additions & 2 deletions cache/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func TestValidCache(t *testing.T) {
t.Fatal(err)
}

cacheItem := cache.Get("test")
cacheItem := cache.GetStack("test")

if cacheItem == nil {
t.Fatal("cache item is nil when it shouldn't be")
Expand All @@ -72,7 +72,7 @@ func TestMissingCacheItem(t *testing.T) {
t.Fatal(err)
}

cacheItem := cache.Get("fake-test")
cacheItem := cache.GetStack("fake-test")

if cacheItem != nil {
t.Fatal("cached item should be nil, as fake-test should be missing.")
Expand Down
89 changes: 89 additions & 0 deletions cmd/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package cmd

import (
"encoding/json"
"fmt"

"github.com/Shackelford-Arden/hctx/cache"
"github.com/Shackelford-Arden/hctx/models"
"github.com/urfave/cli/v2"
)

// CleanCache checks all items in the cache file
// and removes any that have expired or are not valid,
// including stacks that are no longer in the config.
func CleanCache(ctx *cli.Context) error {

newCache := cache.Cache{}
currentCache, err := AppCache.Get()
if err != nil {
return fmt.Errorf("failed to get current cache: %s", err)
}

for stack, stackCache := range currentCache {

// Check if cached stack is in config
configStack := AppConfig.GetStack(stack)
if configStack == nil {
continue
}

cleanCache := models.StackCache{
NomadToken: stackCache.NomadToken,
ConsulToken: stackCache.ConsulToken,
}

// Validate if tokens have expired.
if stackCache.NomadToken != "" && configStack.Nomad != nil {
nomToken := validNomadToken(configStack.Nomad.Address, stackCache.NomadToken)
if !nomToken {
// Remove expired token from cache
cleanCache.NomadToken = ""
}
}

if stackCache.ConsulToken != "" && configStack.Consul != nil {
conToken := validNomadToken(configStack.Consul.Address, stackCache.ConsulToken)
if !conToken {
// Remove expired token from cache
cleanCache.ConsulToken = ""
}
}

newCache[stack] = cleanCache
}

AppCache = &newCache
saveErr := AppCache.Save("")
if saveErr != nil {
return fmt.Errorf("failed to save cache: %s", saveErr)
}

return nil
}

// ClearCache removes all items from the cache file.
func ClearCache(ctx *cli.Context) error {

AppCache = &cache.Cache{}
saveErr := AppCache.Save("")
if saveErr != nil {
return fmt.Errorf("failed to save cleared cache: %s", saveErr)
}

return nil
}

// ShowCache shows the content of the cache file.
func ShowCache(ctx *cli.Context) error {

cache, _ := AppCache.Get()
fmtCache, fmtErr := json.MarshalIndent(cache, "", " ")
if fmtErr != nil {
return fmt.Errorf("failed to read/format the cache: %s", fmtErr.Error())
}

fmt.Println(string(fmtCache))

return nil
}
32 changes: 28 additions & 4 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package cmd

import (
"fmt"
"log/slog"
"os"

"github.com/Shackelford-Arden/hctx/cache"
"github.com/Shackelford-Arden/hctx/config"
"github.com/urfave/cli/v2"
"log/slog"
"os"
)

var AppConfig *config.Config
Expand Down Expand Up @@ -56,13 +57,13 @@ func ValidateConfig(ctx *cli.Context) error {
}

// Get Cache
cache, cacheErr := cache.NewCache("")
cacheItem, cacheErr := cache.NewCache("")
if cacheErr != nil {
return cacheErr
}

AppConfig = cfg
AppCache = cache
AppCache = cacheItem

return nil
}
Expand Down Expand Up @@ -114,6 +115,29 @@ func App() (*cli.App, error) {
Usage: "Used to generate the appropriate shell scripts to set environment variables.",
Args: true,
},
{
Name: "cache",
Aliases: []string{"c"},
Usage: "Interact with the cache.",
Subcommands: []*cli.Command{
{
Name: "show",
Aliases: []string{"s"},
Usage: "ShowCache the current cache",
Action: ShowCache,
},
{
Name: "clear",
Usage: "Clears out the cache of all items.",
Action: ClearCache,
},
{
Name: "clean",
Usage: "Checks all cached items and removes those that have expired.",
Action: CleanCache,
},
},
},
},
}

Expand Down
57 changes: 52 additions & 5 deletions cmd/use.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,69 @@ func Use(ctx *cli.Context) error {
}

currentStack := AppConfig.GetCurrentStack()
// If caching is enabled, set to cache.
// If caching is enabled, cache current stack tokens.
if currentStack != nil && AppConfig.CacheAuth {
currentCache := AppCache.GetStack(currentStack.Name)
toCache := cache.GetCacheableValues()
if toCache.NomadToken != "" {
validToken := validNomadToken(selectedStack.Nomad.Address, toCache.NomadToken)
if validToken {
currentCache.NomadToken = toCache.NomadToken
}
}

if toCache.ConsulToken != "" {
validToken := validConsulToken(selectedStack.Consul.Address, toCache.ConsulToken)
if validToken {
currentCache.ConsulToken = toCache.ConsulToken
}
}
updateErr := AppCache.Update(currentStack.Name, toCache)
if updateErr != nil {
return fmt.Errorf("could not update cache for stack %s: %v", currentStack.Name, updateErr)
}
_ = AppCache.Save("")
}

useOut := unsetTokens(AppConfig.Shell)
var stackCache *models.StackCache
if AppConfig.CacheAuth {
stackCache = AppCache.Get(selectedStack.Name)
currentStackCache := AppCache.GetStack(selectedStack.Name)

// Pull in cache of selected stack, clearing out
// any that are invalid.
if AppConfig.CacheAuth && currentStackCache != nil {

cleanCache := models.StackCache{}

// Validate if tokens have expired.
cleanCache.NomadToken = currentStackCache.NomadToken
if currentStackCache.NomadToken != "" && selectedStack.Nomad != nil {
nomToken := validNomadToken(selectedStack.Nomad.Address, currentStackCache.NomadToken)
if !nomToken {
// Remove expired token from cache
cleanCache.NomadToken = ""
}
}

cleanCache.ConsulToken = currentStackCache.ConsulToken
if currentStackCache.ConsulToken != "" && selectedStack.Consul != nil {
conToken := validNomadToken(selectedStack.Consul.Address, currentStackCache.ConsulToken)
if !conToken {
// Remove expired token from cache
cleanCache.ConsulToken = ""
}
}

err := AppCache.Update(selectedStack.Name, cleanCache)
if err != nil {
return fmt.Errorf("could not update cache for stack %s: %v", selectedStack.Name, err)
}

// Set this so that correct values are pulled in
// later when cached values are pulled in, if enabled
currentStackCache = &cleanCache
}

useOut += selectedStack.Use(AppConfig.Shell, stackCache, AppConfig.CacheAuth)
useOut += selectedStack.Use(AppConfig.Shell, currentStackCache, AppConfig.CacheAuth)

fmt.Println(useOut)

Expand Down
53 changes: 53 additions & 0 deletions cmd/utils.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package cmd

import (
consul "github.com/hashicorp/consul/api"
nomad "github.com/hashicorp/nomad/api"
)

func unsetTokens(shell string) string {

output := ""
Expand All @@ -19,3 +24,51 @@ unset VAULT_TOKEN

return output
}

// validNomadToken checks if the given token is still valid.
// Docs: https://developer.hashicorp.com/nomad/api-docs/acl/tokens#read-self-token
func validNomadToken(addr string, token string) bool {

nom, _ := nomad.NewClient(&nomad.Config{
Address: addr,
})

nomToken, _, err := nom.ACLTokens().Self(&nomad.QueryOptions{AuthToken: token})
if err != nil {
// TODO Consider logging some things to a log file somewhere?
// Preferring to avoid logging text
return false
}

// Likely means the API return a 404 on the token,
// indicating it has expired.
if nomToken == nil {
return false
}

return true
}

// validConsulToken checks to see if the given token is valid anymore.
// Docs: https://developer.hashicorp.com/consul/api-docs/acl/tokens#read-self-token
func validConsulToken(addr string, token string) bool {

con, _ := consul.NewClient(&consul.Config{
Address: addr,
})

consulToken, _, err := con.ACL().TokenReadSelf(&consul.QueryOptions{Token: token})
if err != nil {
// TODO Consider logging some things to a log file somewhere?
// Preferring to avoid logging text
return false
}

// Likely means the API return a 404 on the token,
// indicating it has expired.
if consulToken == nil {
return false
}

return true
}
2 changes: 1 addition & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func NewConfig(cp string) (*Config, error) {
var configPath = cp

if cp == "" {
// Get user homedir
// GetStack user homedir
userHome, homeErr := os.UserHomeDir()
if homeErr != nil {
fmt.Printf("failed to get user homedir: %s", homeErr)
Expand Down
Loading

0 comments on commit 0df03cc

Please sign in to comment.