1package creds 2 3import ( 4 "sync" 5 "time" 6 7 "encoding/json" 8 9 "code.cloudfoundry.org/clock" 10 "code.cloudfoundry.org/lager" 11) 12 13//go:generate counterfeiter . VarSourcePool 14 15type VarSourcePool interface { 16 FindOrCreate(lager.Logger, map[string]interface{}, ManagerFactory) (Secrets, error) 17 Size() int 18 Close() 19} 20 21type inPoolManager struct { 22 manager Manager 23 secrets Secrets 24 lastUseTime time.Time 25 clock clock.Clock 26} 27 28func (m *inPoolManager) close(logger lager.Logger) { 29 m.manager.Close(logger) 30} 31 32func (m *inPoolManager) getSecrets() Secrets { 33 m.lastUseTime = m.clock.Now() 34 return m.secrets 35} 36 37type varSourcePool struct { 38 pool map[string]*inPoolManager 39 lock sync.Mutex 40 credentialManagement CredentialManagementConfig 41 ttl time.Duration 42 clock clock.Clock 43 44 closeOnce sync.Once 45 closed chan struct{} 46} 47 48func NewVarSourcePool( 49 logger lager.Logger, 50 credentialManagement CredentialManagementConfig, 51 ttl time.Duration, 52 collectInterval time.Duration, 53 clock clock.Clock, 54) VarSourcePool { 55 pool := &varSourcePool{ 56 pool: map[string]*inPoolManager{}, 57 lock: sync.Mutex{}, 58 59 credentialManagement: credentialManagement, 60 ttl: ttl, 61 clock: clock, 62 63 closeOnce: sync.Once{}, 64 closed: make(chan struct{}), 65 } 66 67 go pool.collectLoop( 68 logger.Session("collect"), 69 collectInterval, 70 ) 71 72 return pool 73} 74 75func (pool *varSourcePool) Size() int { 76 pool.lock.Lock() 77 defer pool.lock.Unlock() 78 return len(pool.pool) 79} 80 81func (pool *varSourcePool) FindOrCreate(logger lager.Logger, config map[string]interface{}, factory ManagerFactory) (Secrets, error) { 82 b, err := json.Marshal(config) 83 if err != nil { 84 return nil, err 85 } 86 87 key := string(b) 88 89 pool.lock.Lock() 90 defer pool.lock.Unlock() 91 92 if _, ok := pool.pool[key]; !ok { 93 manager, err := factory.NewInstance(config) 94 if err != nil { 95 return nil, err 96 } 97 err = manager.Init(logger) 98 if err != nil { 99 return nil, err 100 } 101 secretsFactory, err := manager.NewSecretsFactory(logger) 102 if err != nil { 103 return nil, err 104 } 105 106 pool.pool[key] = &inPoolManager{ 107 clock: pool.clock, 108 manager: manager, 109 secrets: pool.credentialManagement.NewSecrets(secretsFactory), 110 } 111 } else { 112 logger.Debug("found-existing-credential-manager") 113 } 114 115 return pool.pool[key].getSecrets(), nil 116} 117 118func (pool *varSourcePool) Close() { 119 pool.closeOnce.Do(func() { 120 close(pool.closed) 121 }) 122} 123 124func (pool *varSourcePool) collectLoop(logger lager.Logger, interval time.Duration) { 125 ticker := pool.clock.NewTicker(interval) 126 defer ticker.Stop() 127 128 for { 129 select { 130 case <-pool.closed: 131 pool.collect(logger.Session("close"), true) 132 return 133 case <-ticker.C(): 134 pool.collect(logger.Session("tick"), false) 135 } 136 } 137} 138 139func (pool *varSourcePool) collect(logger lager.Logger, all bool) error { 140 pool.lock.Lock() 141 defer pool.lock.Unlock() 142 143 logger.Debug("before", lager.Data{"size": len(pool.pool)}) 144 145 toDeleteKeys := []string{} 146 for key, manager := range pool.pool { 147 if all || manager.lastUseTime.Add(pool.ttl).Before(pool.clock.Now()) { 148 toDeleteKeys = append(toDeleteKeys, key) 149 manager.close(logger) 150 } 151 } 152 153 for _, key := range toDeleteKeys { 154 delete(pool.pool, key) 155 } 156 157 logger.Debug("after", lager.Data{"size": len(pool.pool)}) 158 159 return nil 160} 161