Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Caching the way I should have from the beginning #11

Merged
merged 3 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Shackelford-Arden marked this conversation as resolved.
Show resolved Hide resolved
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
Loading