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  }