github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/creds/cached_secrets.go (about) 1 package creds 2 3 import ( 4 "time" 5 6 "github.com/patrickmn/go-cache" 7 ) 8 9 type SecretCacheConfig struct { 10 Enabled bool `long:"secret-cache-enabled" description:"Enable in-memory cache for secrets"` 11 Duration time.Duration `long:"secret-cache-duration" default:"1m" description:"If the cache is enabled, secret values will be cached for not longer than this duration (it can be less, if underlying secret lease time is smaller)"` 12 DurationNotFound time.Duration `long:"secret-cache-duration-notfound" default:"10s" description:"If the cache is enabled, secret not found responses will be cached for this duration"` 13 PurgeInterval time.Duration `long:"secret-cache-purge-interval" default:"10m" description:"If the cache is enabled, expired items will be removed on this interval"` 14 } 15 16 type CachedSecrets struct { 17 secrets Secrets 18 cacheConfig SecretCacheConfig 19 cache *cache.Cache 20 } 21 22 type CacheEntry struct { 23 value interface{} 24 expiration *time.Time 25 found bool 26 } 27 28 func NewCachedSecrets(secrets Secrets, cacheConfig SecretCacheConfig) *CachedSecrets { 29 // Create a cache with: 30 // - default expiration time for entries set to 'cacheConfig.Duration' 31 // - purges expired items regularly, on every `cacheConfig.PurgeInterval` after creation 32 return &CachedSecrets{ 33 secrets: secrets, 34 cacheConfig: cacheConfig, 35 cache: cache.New(cacheConfig.Duration, cacheConfig.PurgeInterval), 36 } 37 } 38 39 func (cs *CachedSecrets) Get(secretPath string) (interface{}, *time.Time, bool, error) { 40 // if there is a corresponding entry in the cache, return it 41 entry, found := cs.cache.Get(secretPath) 42 if found { 43 result := entry.(CacheEntry) 44 return result.value, result.expiration, result.found, nil 45 } 46 47 // otherwise, let's make a request to the underlying secret manager 48 value, expiration, found, err := cs.secrets.Get(secretPath) 49 50 // we don't want to cache errors, let the errors be retried the next time around 51 if err != nil { 52 return nil, nil, false, err 53 } 54 55 // here we want to cache secret value, expiration, and found flag too 56 // meaning that "secret not found" responses will be cached too! 57 entry = CacheEntry{value: value, expiration: expiration, found: found} 58 59 if found { 60 // take default cache ttl 61 duration := cs.cacheConfig.Duration 62 if expiration != nil { 63 // if secret lease time expires sooner, make duration smaller than default duration 64 itemDuration := expiration.Sub(time.Now()) 65 if itemDuration < duration { 66 duration = itemDuration 67 } 68 } 69 cs.cache.Set(secretPath, entry, duration) 70 } else { 71 cs.cache.Set(secretPath, entry, cs.cacheConfig.DurationNotFound) 72 } 73 74 return value, expiration, found, nil 75 } 76 77 func (cs *CachedSecrets) NewSecretLookupPaths(teamName string, pipelineName string, allowRootPath bool) []SecretLookupPath { 78 return cs.secrets.NewSecretLookupPaths(teamName, pipelineName, allowRootPath) 79 }