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

     1  package creds_test
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	"github.com/pf-qiu/concourse/v6/atc/creds"
     8  	"github.com/pf-qiu/concourse/v6/atc/creds/credsfakes"
     9  
    10  	. "github.com/onsi/ginkgo"
    11  	. "github.com/onsi/gomega"
    12  )
    13  
    14  func makeGetStub(name string, value interface{}, expiration *time.Time, found bool, err error, cntReads *int, cntMisses *int) func(string) (interface{}, *time.Time, bool, error) {
    15  	return func(secretPath string) (interface{}, *time.Time, bool, error) {
    16  		if secretPath == name {
    17  			*cntReads++
    18  			return value, expiration, found, err
    19  		}
    20  		*cntMisses++
    21  		return nil, nil, false, nil
    22  	}
    23  }
    24  
    25  var _ = Describe("Caching of secrets", func() {
    26  
    27  	var secretManager *credsfakes.FakeSecrets
    28  	var cacheConfig creds.SecretCacheConfig
    29  	var cachedSecretManager *creds.CachedSecrets
    30  	var underlyingReads int
    31  	var underlyingMisses int
    32  
    33  	BeforeEach(func() {
    34  		secretManager = new(credsfakes.FakeSecrets)
    35  		cacheConfig = creds.SecretCacheConfig{
    36  			Duration:         2 * time.Second,
    37  			DurationNotFound: 1 * time.Second,
    38  			PurgeInterval:    100 * time.Millisecond,
    39  		}
    40  		cachedSecretManager = creds.NewCachedSecrets(secretManager, cacheConfig)
    41  		underlyingReads = 0
    42  		underlyingMisses = 0
    43  	})
    44  
    45  	It("should handle missing secrets correctly and cache misses", func() {
    46  		secretManager.GetStub = makeGetStub("foo", "value", nil, true, nil, &underlyingReads, &underlyingMisses)
    47  
    48  		// miss
    49  		value, expiration, found, err := cachedSecretManager.Get("bar")
    50  		Expect(value).To(BeNil())
    51  		Expect(expiration).To(BeNil())
    52  		Expect(found).To(BeFalse())
    53  		Expect(err).To(BeNil())
    54  		Expect(underlyingReads).To(BeIdenticalTo(0))
    55  		Expect(underlyingMisses).To(BeIdenticalTo(1))
    56  
    57  		// cached miss
    58  		value, expiration, found, err = cachedSecretManager.Get("bar")
    59  		Expect(value).To(BeNil())
    60  		Expect(expiration).To(BeNil())
    61  		Expect(found).To(BeFalse())
    62  		Expect(err).To(BeNil())
    63  		Expect(underlyingReads).To(BeIdenticalTo(0))
    64  		Expect(underlyingMisses).To(BeIdenticalTo(1))
    65  	})
    66  
    67  	It("should handle existing secrets correctly and cache them, returning previous value if the underlying value has changed", func() {
    68  		secretManager.GetStub = makeGetStub("foo", "value", nil, true, nil, &underlyingReads, &underlyingMisses)
    69  
    70  		// hit
    71  		value, expiration, found, err := cachedSecretManager.Get("foo")
    72  		Expect(value).To(BeIdenticalTo("value"))
    73  		Expect(expiration).To(BeNil())
    74  		Expect(found).To(BeTrue())
    75  		Expect(err).To(BeNil())
    76  		Expect(underlyingReads).To(BeIdenticalTo(1))
    77  		Expect(underlyingMisses).To(BeIdenticalTo(0))
    78  
    79  		// cached hit
    80  		secretManager.GetStub = makeGetStub("foo", "different-value", nil, true, nil, &underlyingReads, &underlyingMisses)
    81  		value, expiration, found, err = cachedSecretManager.Get("foo")
    82  		Expect(value).To(BeIdenticalTo("value"))
    83  		Expect(expiration).To(BeNil())
    84  		Expect(found).To(BeTrue())
    85  		Expect(err).To(BeNil())
    86  		Expect(underlyingReads).To(BeIdenticalTo(1))
    87  		Expect(underlyingMisses).To(BeIdenticalTo(0))
    88  	})
    89  
    90  	It("should handle errors correctly and avoid caching errors", func() {
    91  		secretManager.GetStub = makeGetStub("baz", nil, nil, false, fmt.Errorf("unexpected error"), &underlyingReads, &underlyingMisses)
    92  
    93  		// error
    94  		value, expiration, found, err := cachedSecretManager.Get("baz")
    95  		Expect(value).To(BeNil())
    96  		Expect(expiration).To(BeNil())
    97  		Expect(found).To(BeFalse())
    98  		Expect(err).NotTo(BeNil())
    99  		Expect(underlyingReads).To(BeIdenticalTo(1))
   100  		Expect(underlyingMisses).To(BeIdenticalTo(0))
   101  
   102  		// no caching of error
   103  		value, expiration, found, err = cachedSecretManager.Get("baz")
   104  		Expect(value).To(BeNil())
   105  		Expect(expiration).To(BeNil())
   106  		Expect(found).To(BeFalse())
   107  		Expect(err).NotTo(BeNil())
   108  		Expect(underlyingReads).To(BeIdenticalTo(2))
   109  		Expect(underlyingMisses).To(BeIdenticalTo(0))
   110  	})
   111  
   112  	It("should re-retrieve expired entries", func() {
   113  		secretManager.GetStub = makeGetStub("foo", "value", nil, true, nil, &underlyingReads, &underlyingMisses)
   114  
   115  		// get few entries first
   116  		_, _, _, _ = cachedSecretManager.Get("foo")
   117  		_, _, _, _ = cachedSecretManager.Get("bar")
   118  		_, _, _, _ = cachedSecretManager.Get("baz")
   119  		Expect(underlyingReads).To(BeIdenticalTo(1))
   120  		Expect(underlyingMisses).To(BeIdenticalTo(2))
   121  
   122  		// get these entries again and make sure they are cached
   123  		_, _, _, _ = cachedSecretManager.Get("foo")
   124  		_, _, _, _ = cachedSecretManager.Get("bar")
   125  		_, _, _, _ = cachedSecretManager.Get("baz")
   126  		Expect(underlyingReads).To(BeIdenticalTo(1))
   127  		Expect(underlyingMisses).To(BeIdenticalTo(2))
   128  
   129  		// sleep
   130  		time.Sleep(cacheConfig.Duration + time.Millisecond)
   131  
   132  		// check counters again and make sure the entries are re-retrieved
   133  		_, _, _, _ = cachedSecretManager.Get("foo")
   134  		_, _, _, _ = cachedSecretManager.Get("bar")
   135  		_, _, _, _ = cachedSecretManager.Get("baz")
   136  		Expect(underlyingReads).To(BeIdenticalTo(2))
   137  		Expect(underlyingMisses).To(BeIdenticalTo(4))
   138  	})
   139  
   140  	It("should cache negative responses for a separately specified duration", func() {
   141  		secretManager.GetStub = makeGetStub("foo", "value", nil, true, nil, &underlyingReads, &underlyingMisses)
   142  
   143  		// get few entries first
   144  		_, _, _, _ = cachedSecretManager.Get("foo")
   145  		_, _, _, _ = cachedSecretManager.Get("bar")
   146  		_, _, _, _ = cachedSecretManager.Get("baz")
   147  		Expect(underlyingReads).To(BeIdenticalTo(1))
   148  		Expect(underlyingMisses).To(BeIdenticalTo(2))
   149  
   150  		// sleep
   151  		time.Sleep(cacheConfig.DurationNotFound + time.Millisecond)
   152  
   153  		// existing secret should still be cached
   154  		_, _, _, _ = cachedSecretManager.Get("foo")
   155  		Expect(underlyingReads).To(BeIdenticalTo(1))
   156  		Expect(underlyingMisses).To(BeIdenticalTo(2))
   157  
   158  		// non-existing secrets should be attempted to be retrieved again
   159  		_, _, _, _ = cachedSecretManager.Get("bar")
   160  		_, _, _, _ = cachedSecretManager.Get("baz")
   161  		Expect(underlyingReads).To(BeIdenticalTo(1))
   162  		Expect(underlyingMisses).To(BeIdenticalTo(4))
   163  	})
   164  
   165  })