github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/creds/pool.go (about)

     1  package creds
     2  
     3  import (
     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  
    15  type VarSourcePool interface {
    16  	FindOrCreate(lager.Logger, map[string]interface{}, ManagerFactory) (Secrets, error)
    17  	Size() int
    18  	Close()
    19  }
    20  
    21  type inPoolManager struct {
    22  	manager     Manager
    23  	secrets     Secrets
    24  	lastUseTime time.Time
    25  	clock       clock.Clock
    26  }
    27  
    28  func (m *inPoolManager) close(logger lager.Logger) {
    29  	m.manager.Close(logger)
    30  }
    31  
    32  func (m *inPoolManager) getSecrets() Secrets {
    33  	m.lastUseTime = m.clock.Now()
    34  	return m.secrets
    35  }
    36  
    37  type 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  
    48  func 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  
    75  func (pool *varSourcePool) Size() int {
    76  	pool.lock.Lock()
    77  	defer pool.lock.Unlock()
    78  	return len(pool.pool)
    79  }
    80  
    81  func (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  
   118  func (pool *varSourcePool) Close() {
   119  	pool.closeOnce.Do(func() {
   120  		close(pool.closed)
   121  	})
   122  }
   123  
   124  func (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  
   139  func (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  }