github.com/askholme/packer@v0.7.2-0.20140924152349-70d9566a6852/packer/cache.go (about) 1 package packer 2 3 import ( 4 "crypto/sha256" 5 "encoding/hex" 6 "log" 7 "os" 8 "path/filepath" 9 "strings" 10 "sync" 11 ) 12 13 // Cache implements a caching interface where files can be stored for 14 // re-use between multiple runs. 15 type Cache interface { 16 // Lock takes a key and returns the path where the file can be written to. 17 // Packer guarantees that no other process will write to this file while 18 // the lock is held. 19 // 20 // If the key has an extension (e.g., file.ext), the resulting path 21 // will have that extension as well. 22 // 23 // The cache will block and wait for the lock. 24 Lock(string) string 25 26 // Unlock will unlock a certain cache key. Be very careful that this 27 // is only called once per lock obtained. 28 Unlock(string) 29 30 // RLock returns the path to a key in the cache and locks it for reading. 31 // The second return parameter is whether the key existed or not. 32 // This will block if any locks are held for writing. No lock will be 33 // held if the key doesn't exist. 34 RLock(string) (string, bool) 35 36 // RUnlock will unlock a key for reading. 37 RUnlock(string) 38 } 39 40 // FileCache implements a Cache by caching the data directly to a cache 41 // directory. 42 type FileCache struct { 43 CacheDir string 44 l sync.Mutex 45 rw map[string]*sync.RWMutex 46 } 47 48 func (f *FileCache) Lock(key string) string { 49 hashKey := f.hashKey(key) 50 rw := f.rwLock(hashKey) 51 rw.Lock() 52 53 return f.cachePath(key, hashKey) 54 } 55 56 func (f *FileCache) Unlock(key string) { 57 hashKey := f.hashKey(key) 58 rw := f.rwLock(hashKey) 59 rw.Unlock() 60 } 61 62 func (f *FileCache) RLock(key string) (string, bool) { 63 hashKey := f.hashKey(key) 64 rw := f.rwLock(hashKey) 65 rw.RLock() 66 67 return f.cachePath(key, hashKey), true 68 } 69 70 func (f *FileCache) RUnlock(key string) { 71 hashKey := f.hashKey(key) 72 rw := f.rwLock(hashKey) 73 rw.RUnlock() 74 } 75 76 func (f *FileCache) cachePath(key string, hashKey string) string { 77 if endIndex := strings.Index(key, "?"); endIndex > -1 { 78 key = key[:endIndex] 79 } 80 81 suffix := "" 82 dotIndex := strings.LastIndex(key, ".") 83 if dotIndex > -1 { 84 if slashIndex := strings.LastIndex(key, "/"); slashIndex <= dotIndex { 85 suffix = key[dotIndex:] 86 } 87 } 88 89 // Make the cache directory. We ignore errors here, but 90 // log them in case something happens. 91 if err := os.MkdirAll(f.CacheDir, 0755); err != nil { 92 log.Printf( 93 "[ERR] Error making cacheDir: %s %s", f.CacheDir, err) 94 } 95 96 return filepath.Join(f.CacheDir, hashKey+suffix) 97 } 98 99 func (f *FileCache) hashKey(key string) string { 100 sha := sha256.New() 101 sha.Write([]byte(key)) 102 return hex.EncodeToString(sha.Sum(nil)) 103 } 104 105 func (f *FileCache) rwLock(hashKey string) *sync.RWMutex { 106 f.l.Lock() 107 defer f.l.Unlock() 108 109 if f.rw == nil { 110 f.rw = make(map[string]*sync.RWMutex) 111 } 112 113 if result, ok := f.rw[hashKey]; ok { 114 return result 115 } 116 117 var result sync.RWMutex 118 f.rw[hashKey] = &result 119 return &result 120 }