go.undefinedlabs.com/scopeagent@v0.4.2/agent/cache.go (about) 1 package agent 2 3 import ( 4 "crypto/sha1" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io/ioutil" 9 "log" 10 "os" 11 "path/filepath" 12 "runtime" 13 "sync" 14 "time" 15 16 "github.com/mitchellh/go-homedir" 17 ) 18 19 const cacheTimeout = 1 * time.Minute 20 21 type ( 22 localCache struct { 23 m sync.Mutex 24 tenant interface{} 25 basePath string 26 timeout time.Duration 27 debugMode bool 28 logger *log.Logger 29 } 30 cacheItem struct { 31 Value interface{} 32 } 33 ) 34 35 // Create a new local cache 36 func newLocalCache(tenant interface{}, timeout time.Duration, debugMode bool, logger *log.Logger) *localCache { 37 lc := &localCache{ 38 timeout: timeout, 39 debugMode: debugMode, 40 logger: logger, 41 } 42 lc.SetTenant(tenant) 43 return lc 44 } 45 46 // Gets or sets a local cache value 47 func (c *localCache) GetOrSet(key string, useTimeout bool, fn func(interface{}, string) interface{}) interface{} { 48 c.m.Lock() 49 defer c.m.Unlock() 50 51 // Loader function 52 loaderFunc := func(key string, err error, fn func(interface{}, string) interface{}) interface{} { 53 if err != nil { 54 c.logger.Printf("Local cache: %v", err) 55 } 56 if fn == nil { 57 return nil 58 } 59 60 // Call the loader 61 resp := fn(c.tenant, key) 62 63 if resp != nil { 64 path := fmt.Sprintf("%s.%s", c.basePath, key) 65 cItem := &cacheItem{Value: resp} 66 // Save a local cache for the response 67 if data, err := json.Marshal(cItem); err == nil { 68 if c.debugMode { 69 c.logger.Printf("Local cache saving: %s => %s", path, string(data)) 70 } 71 if err := ioutil.WriteFile(path, data, 0755); err != nil { 72 c.logger.Printf("Error writing json file: %v", err) 73 } 74 } 75 } 76 77 return resp 78 } 79 80 path := fmt.Sprintf("%s.%s", c.basePath, key) 81 82 // We try to load the cached value 83 file, err := os.Open(path) 84 if err != nil { 85 return loaderFunc(key, err, fn) 86 } 87 defer file.Close() 88 89 // Checks if the cache data is old 90 if useTimeout { 91 fInfo, err := file.Stat() 92 if err != nil { 93 return loaderFunc(key, err, fn) 94 } 95 sTime := time.Now().Sub(fInfo.ModTime()) 96 if sTime > cacheTimeout { 97 err = errors.New(fmt.Sprintf("The local cache key '%s' has timeout: %v", path, sTime)) 98 return loaderFunc(key, err, fn) 99 } 100 } 101 102 // Read the cached value 103 fileBytes, err := ioutil.ReadAll(file) 104 if err != nil { 105 return loaderFunc(key, err, fn) 106 } 107 108 // Unmarshal json data 109 var cItem cacheItem 110 if err := json.Unmarshal(fileBytes, &cItem); err != nil { 111 return loaderFunc(key, err, fn) 112 } else { 113 c.logger.Printf("Local cache loaded: %s (%d bytes)", path, len(fileBytes)) 114 return cItem.Value 115 } 116 } 117 118 // Sets the local cache tenant 119 func (c *localCache) SetTenant(tenant interface{}) { 120 homeDir, err := homedir.Dir() 121 if err != nil { 122 c.logger.Printf("local cache error: %v", err) 123 return 124 } 125 data, err := json.Marshal(tenant) 126 if err != nil { 127 c.logger.Printf("local cache error: %v", err) 128 return 129 } 130 hash := fmt.Sprintf("%x", sha1.Sum(data)) 131 132 var folder string 133 if runtime.GOOS == "windows" { 134 folder = fmt.Sprintf("%s/AppData/Roaming/scope/cache", homeDir) 135 } else { 136 folder = fmt.Sprintf("%s/.scope/cache", homeDir) 137 } 138 139 if _, err := os.Stat(folder); err == nil { 140 c.tenant = tenant 141 c.basePath = filepath.Join(folder, hash) 142 } else if os.IsNotExist(err) { 143 err = os.MkdirAll(folder, 0755) 144 if err != nil { 145 c.logger.Printf("local cache error: %v", err) 146 return 147 } 148 c.tenant = tenant 149 c.basePath = filepath.Join(folder, hash) 150 } else { 151 c.logger.Printf("local cache error: %v", err) 152 } 153 }