Merge pull request #85370 from mikedanese/expcache

fix terrible bug and inline GC for expiring cache
This commit is contained in:
Kubernetes Prow Robot 2019-11-16 00:29:40 -08:00 committed by GitHub
commit e0529008d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 144 additions and 117 deletions

View File

@ -17,7 +17,6 @@ limitations under the License.
package authenticator package authenticator
import ( import (
"context"
"time" "time"
"github.com/go-openapi/spec" "github.com/go-openapi/spec"
@ -193,7 +192,7 @@ func (config Config) New() (authenticator.Request, *spec.SecurityDefinitions, er
tokenAuth := tokenunion.New(tokenAuthenticators...) tokenAuth := tokenunion.New(tokenAuthenticators...)
// Optionally cache authentication results // Optionally cache authentication results
if config.TokenSuccessCacheTTL > 0 || config.TokenFailureCacheTTL > 0 { if config.TokenSuccessCacheTTL > 0 || config.TokenFailureCacheTTL > 0 {
tokenAuth = tokencache.New(context.TODO(), tokenAuth, true, config.TokenSuccessCacheTTL, config.TokenFailureCacheTTL) tokenAuth = tokencache.New(tokenAuth, true, config.TokenSuccessCacheTTL, config.TokenFailureCacheTTL)
} }
authenticators = append(authenticators, bearertoken.New(tokenAuth), websocket.NewProtocolAuthenticator(tokenAuth)) authenticators = append(authenticators, bearertoken.New(tokenAuth), websocket.NewProtocolAuthenticator(tokenAuth))
securityDefinitions["BearerToken"] = &spec.SecurityScheme{ securityDefinitions["BearerToken"] = &spec.SecurityScheme{
@ -313,5 +312,5 @@ func newWebhookTokenAuthenticator(webhookConfigFile string, version string, ttl
return nil, err return nil, err
} }
return tokencache.New(context.TODO(), webhookTokenAuthenticator, false, ttl, ttl), nil return tokencache.New(webhookTokenAuthenticator, false, ttl, ttl), nil
} }

View File

@ -18,15 +18,13 @@ package cache
import ( import (
"container/heap" "container/heap"
"context"
"sync" "sync"
"time" "time"
utilclock "k8s.io/apimachinery/pkg/util/clock" utilclock "k8s.io/apimachinery/pkg/util/clock"
) )
// NewExpiring returns an initialized expiring cache. Users must call // NewExpiring returns an initialized expiring cache.
// (*Expiring).Run() to begin the GC goroutine.
func NewExpiring() *Expiring { func NewExpiring() *Expiring {
return NewExpiringWithClock(utilclock.RealClock{}) return NewExpiringWithClock(utilclock.RealClock{})
} }
@ -72,7 +70,7 @@ func (c *Expiring) Get(key interface{}) (val interface{}, ok bool) {
c.mu.RLock() c.mu.RLock()
defer c.mu.RUnlock() defer c.mu.RUnlock()
e, ok := c.cache[key] e, ok := c.cache[key]
if !ok || c.clock.Now().After(e.expiry) { if !ok || !c.clock.Now().Before(e.expiry) {
return nil, false return nil, false
} }
return e.val, true return e.val, true
@ -80,9 +78,13 @@ func (c *Expiring) Get(key interface{}) (val interface{}, ok bool) {
// Set sets a key/value/expiry entry in the map, overwriting any previous entry // Set sets a key/value/expiry entry in the map, overwriting any previous entry
// with the same key. The entry expires at the given expiry time, but its TTL // with the same key. The entry expires at the given expiry time, but its TTL
// may be lengthened or shortened by additional calls to Set(). // may be lengthened or shortened by additional calls to Set(). Garbage
// collection of expired entries occurs during calls to Set(), however calls to
// Get() will not return expired entries that have not yet been garbage
// collected.
func (c *Expiring) Set(key interface{}, val interface{}, ttl time.Duration) { func (c *Expiring) Set(key interface{}, val interface{}, ttl time.Duration) {
expiry := c.clock.Now().Add(ttl) now := c.clock.Now()
expiry := now.Add(ttl)
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
@ -95,6 +97,9 @@ func (c *Expiring) Set(key interface{}, val interface{}, ttl time.Duration) {
generation: c.generation, generation: c.generation,
} }
// Run GC inline before pushing the new entry.
c.gc(now)
heap.Push(&c.heap, &expiringHeapEntry{ heap.Push(&c.heap, &expiringHeapEntry{
key: key, key: key,
generation: c.generation, generation: c.generation,
@ -134,28 +139,7 @@ func (c *Expiring) Len() int {
return len(c.cache) return len(c.cache)
} }
const gcInterval = 50 * time.Millisecond func (c *Expiring) gc(now time.Time) {
// Run runs the GC goroutine. The goroutine exits when the passed in context is
// cancelled.
func (c *Expiring) Run(ctx context.Context) {
t := c.clock.NewTicker(gcInterval)
defer t.Stop()
for {
select {
case <-t.C():
c.gc()
case <-ctx.Done():
return
}
}
}
func (c *Expiring) gc() {
now := c.clock.Now()
c.mu.Lock()
defer c.mu.Unlock()
for { for {
// Return from gc if the heap is empty or the next element is not yet // Return from gc if the heap is empty or the next element is not yet
// expired. // expired.
@ -164,7 +148,7 @@ func (c *Expiring) gc() {
// from looking at the (*expiringHeap).Pop() implmentation below. // from looking at the (*expiringHeap).Pop() implmentation below.
// heap.Pop() swaps the first entry with the last entry of the heap, then // heap.Pop() swaps the first entry with the last entry of the heap, then
// calls (*expiringHeap).Pop() which returns the last element. // calls (*expiringHeap).Pop() which returns the last element.
if len(c.heap) == 0 || now.After(c.heap[0].expiry) { if len(c.heap) == 0 || now.Before(c.heap[0].expiry) {
return return
} }
cleanup := heap.Pop(&c.heap).(*expiringHeapEntry) cleanup := heap.Pop(&c.heap).(*expiringHeapEntry)

View File

@ -29,11 +29,7 @@ import (
) )
func TestExpiringCache(t *testing.T) { func TestExpiringCache(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
cache := NewExpiring() cache := NewExpiring()
go cache.Run(ctx)
if result, ok := cache.Get("foo"); ok || result != nil { if result, ok := cache.Get("foo"); ok || result != nil {
t.Errorf("Expected null, false, got %#v, %v", result, ok) t.Errorf("Expected null, false, got %#v, %v", result, ok)
@ -62,12 +58,8 @@ func TestExpiringCache(t *testing.T) {
} }
func TestExpiration(t *testing.T) { func TestExpiration(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
fc := &utilclock.FakeClock{} fc := &utilclock.FakeClock{}
c := NewExpiringWithClock(fc) c := NewExpiringWithClock(fc)
go c.Run(ctx)
c.Set("a", "a", time.Second) c.Set("a", "a", time.Second)
@ -100,12 +92,10 @@ func TestExpiration(t *testing.T) {
// remove the key. // remove the key.
c.Set("a", "a", time.Second) c.Set("a", "a", time.Second)
c.mu.Lock()
e := c.cache["a"] e := c.cache["a"]
e.generation++ e.generation++
e.expiry = e.expiry.Add(1 * time.Second) e.expiry = e.expiry.Add(1 * time.Second)
c.cache["a"] = e c.cache["a"] = e
c.mu.Unlock()
fc.Step(1 * time.Second) fc.Step(1 * time.Second)
if _, ok := c.Get("a"); !ok { if _, ok := c.Get("a"); !ok {
@ -113,6 +103,107 @@ func TestExpiration(t *testing.T) {
} }
} }
func TestGarbageCollection(t *testing.T) {
fc := &utilclock.FakeClock{}
type entry struct {
key, val string
ttl time.Duration
}
tests := []struct {
name string
now time.Time
set []entry
want map[string]string
}{
{
name: "two entries just set",
now: fc.Now().Add(0 * time.Second),
set: []entry{
{"a", "aa", 1 * time.Second},
{"b", "bb", 2 * time.Second},
},
want: map[string]string{
"a": "aa",
"b": "bb",
},
},
{
name: "first entry expired now",
now: fc.Now().Add(1 * time.Second),
set: []entry{
{"a", "aa", 1 * time.Second},
{"b", "bb", 2 * time.Second},
},
want: map[string]string{
"b": "bb",
},
},
{
name: "first entry expired half a second ago",
now: fc.Now().Add(1500 * time.Millisecond),
set: []entry{
{"a", "aa", 1 * time.Second},
{"b", "bb", 2 * time.Second},
},
want: map[string]string{
"b": "bb",
},
},
{
name: "three entries weird order",
now: fc.Now().Add(1 * time.Second),
set: []entry{
{"c", "cc", 3 * time.Second},
{"a", "aa", 1 * time.Second},
{"b", "bb", 2 * time.Second},
},
want: map[string]string{
"b": "bb",
"c": "cc",
},
},
{
name: "expire multiple entries in one cycle",
now: fc.Now().Add(2500 * time.Millisecond),
set: []entry{
{"a", "aa", 1 * time.Second},
{"b", "bb", 2 * time.Second},
{"c", "cc", 3 * time.Second},
},
want: map[string]string{
"c": "cc",
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
c := NewExpiringWithClock(fc)
for _, e := range test.set {
c.Set(e.key, e.val, e.ttl)
}
c.gc(test.now)
for k, want := range test.want {
got, ok := c.Get(k)
if !ok {
t.Errorf("expected cache to have entry for key=%q but found none", k)
continue
}
if got != want {
t.Errorf("unexpected value for key=%q: got=%q, want=%q", k, got, want)
}
}
if got, want := c.Len(), len(test.want); got != want {
t.Errorf("unexpected cache size: got=%d, want=%d", got, want)
}
})
}
}
func BenchmarkExpiringCacheContention(b *testing.B) { func BenchmarkExpiringCacheContention(b *testing.B) {
b.Run("evict_probablility=100%", func(b *testing.B) { b.Run("evict_probablility=100%", func(b *testing.B) {
benchmarkExpiringCacheContention(b, 1) benchmarkExpiringCacheContention(b, 1)
@ -126,12 +217,8 @@ func BenchmarkExpiringCacheContention(b *testing.B) {
} }
func benchmarkExpiringCacheContention(b *testing.B, prob float64) { func benchmarkExpiringCacheContention(b *testing.B, prob float64) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
const numKeys = 1 << 16 const numKeys = 1 << 16
cache := NewExpiring() cache := NewExpiring()
go cache.Run(ctx)
keys := []string{} keys := []string{}
for i := 0; i < numKeys; i++ { for i := 0; i < numKeys; i++ {
@ -166,7 +253,6 @@ func TestStressExpiringCache(t *testing.T) {
const numKeys = 1 << 16 const numKeys = 1 << 16
cache := NewExpiring() cache := NewExpiring()
go cache.Run(ctx)
keys := []string{} keys := []string{}
for i := 0; i < numKeys; i++ { for i := 0; i < numKeys; i++ {

View File

@ -17,7 +17,6 @@ limitations under the License.
package authenticatorfactory package authenticatorfactory
import ( import (
"context"
"errors" "errors"
"time" "time"
@ -84,7 +83,7 @@ func (c DelegatingAuthenticatorConfig) New() (authenticator.Request, *spec.Secur
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
cachingTokenAuth := cache.New(context.TODO(), tokenAuth, false, c.CacheTTL, c.CacheTTL) cachingTokenAuth := cache.New(tokenAuth, false, c.CacheTTL, c.CacheTTL)
authenticators = append(authenticators, bearertoken.New(cachingTokenAuth), websocket.NewProtocolAuthenticator(cachingTokenAuth)) authenticators = append(authenticators, bearertoken.New(cachingTokenAuth), websocket.NewProtocolAuthenticator(cachingTokenAuth))
securityDefinitions["BearerToken"] = &spec.SecurityScheme{ securityDefinitions["BearerToken"] = &spec.SecurityScheme{

View File

@ -17,7 +17,6 @@ limitations under the License.
package cache package cache
import ( import (
"context"
"time" "time"
utilcache "k8s.io/apimachinery/pkg/util/cache" utilcache "k8s.io/apimachinery/pkg/util/cache"
@ -28,10 +27,8 @@ type simpleCache struct {
cache *utilcache.Expiring cache *utilcache.Expiring
} }
func newSimpleCache(ctx context.Context, clock clock.Clock) cache { func newSimpleCache(clock clock.Clock) cache {
c := &simpleCache{cache: utilcache.NewExpiringWithClock(clock)} return &simpleCache{cache: utilcache.NewExpiringWithClock(clock)}
go c.cache.Run(ctx)
return c
} }
func (c *simpleCache) get(key string) (*cacheRecord, bool) { func (c *simpleCache) get(key string) (*cacheRecord, bool) {

View File

@ -17,7 +17,6 @@ limitations under the License.
package cache package cache
import ( import (
"context"
"fmt" "fmt"
"math/rand" "math/rand"
"testing" "testing"
@ -31,9 +30,7 @@ import (
) )
func TestSimpleCache(t *testing.T) { func TestSimpleCache(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) testCache(newSimpleCache(clock.RealClock{}), t)
defer cancel()
testCache(newSimpleCache(ctx, clock.RealClock{}), t)
} }
// Note: the performance profile of this benchmark may not match that in the production. // Note: the performance profile of this benchmark may not match that in the production.
@ -42,22 +39,16 @@ func TestSimpleCache(t *testing.T) {
func BenchmarkCacheContentions(b *testing.B) { func BenchmarkCacheContentions(b *testing.B) {
for _, numKeys := range []int{1 << 8, 1 << 12, 1 << 16} { for _, numKeys := range []int{1 << 8, 1 << 12, 1 << 16} {
b.Run(fmt.Sprintf("Simple/keys=%d", numKeys), func(b *testing.B) { b.Run(fmt.Sprintf("Simple/keys=%d", numKeys), func(b *testing.B) {
ctx, cancel := context.WithCancel(context.Background()) benchmarkCache(newSimpleCache(clock.RealClock{}), b, numKeys)
defer cancel()
benchmarkCache(newSimpleCache(ctx, clock.RealClock{}), b, numKeys)
}) })
b.Run(fmt.Sprintf("Striped/keys=%d", numKeys), func(b *testing.B) { b.Run(fmt.Sprintf("Striped/keys=%d", numKeys), func(b *testing.B) {
ctx, cancel := context.WithCancel(context.Background()) benchmarkCache(newStripedCache(32, fnvHashFunc, func() cache { return newSimpleCache(clock.RealClock{}) }), b, numKeys)
defer cancel()
benchmarkCache(newStripedCache(32, fnvHashFunc, func() cache { return newSimpleCache(ctx, clock.RealClock{}) }), b, numKeys)
}) })
} }
} }
func TestStripedCache(t *testing.T) { func TestStripedCache(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) testCache(newStripedCache(32, fnvHashFunc, func() cache { return newSimpleCache(clock.RealClock{}) }), t)
defer cancel()
testCache(newStripedCache(32, fnvHashFunc, func() cache { return newSimpleCache(ctx, clock.RealClock{}) }), t)
} }
func benchmarkCache(cache cache, b *testing.B, numKeys int) { func benchmarkCache(cache cache, b *testing.B, numKeys int) {

View File

@ -64,11 +64,11 @@ type cache interface {
} }
// New returns a token authenticator that caches the results of the specified authenticator. A ttl of 0 bypasses the cache. // New returns a token authenticator that caches the results of the specified authenticator. A ttl of 0 bypasses the cache.
func New(ctx context.Context, authenticator authenticator.Token, cacheErrs bool, successTTL, failureTTL time.Duration) authenticator.Token { func New(authenticator authenticator.Token, cacheErrs bool, successTTL, failureTTL time.Duration) authenticator.Token {
return newWithClock(ctx, authenticator, cacheErrs, successTTL, failureTTL, utilclock.RealClock{}) return newWithClock(authenticator, cacheErrs, successTTL, failureTTL, utilclock.RealClock{})
} }
func newWithClock(ctx context.Context, authenticator authenticator.Token, cacheErrs bool, successTTL, failureTTL time.Duration, clock utilclock.Clock) authenticator.Token { func newWithClock(authenticator authenticator.Token, cacheErrs bool, successTTL, failureTTL time.Duration, clock utilclock.Clock) authenticator.Token {
randomCacheKey := make([]byte, 32) randomCacheKey := make([]byte, 32)
if _, err := rand.Read(randomCacheKey); err != nil { if _, err := rand.Read(randomCacheKey); err != nil {
panic(err) // rand should never fail panic(err) // rand should never fail
@ -86,7 +86,7 @@ func newWithClock(ctx context.Context, authenticator authenticator.Token, cacheE
// used. Currently we advertise support 5k nodes and 10k // used. Currently we advertise support 5k nodes and 10k
// namespaces; a 32k entry cache is therefore a 2x safety // namespaces; a 32k entry cache is therefore a 2x safety
// margin. // margin.
cache: newStripedCache(32, fnvHashFunc, func() cache { return newSimpleCache(ctx, clock) }), cache: newStripedCache(32, fnvHashFunc, func() cache { return newSimpleCache(clock) }),
hashPool: &sync.Pool{ hashPool: &sync.Pool{
New: func() interface{} { New: func() interface{} {

View File

@ -50,10 +50,7 @@ func TestCachedTokenAuthenticator(t *testing.T) {
}) })
fakeClock := utilclock.NewFakeClock(time.Now()) fakeClock := utilclock.NewFakeClock(time.Now())
ctx, cancel := context.WithCancel(context.Background()) a := newWithClock(fakeAuth, true, time.Minute, 0, fakeClock)
defer cancel()
a := newWithClock(ctx, fakeAuth, true, time.Minute, 0, fakeClock)
calledWithToken, resultUsers, resultOk, resultErr = []string{}, nil, false, nil calledWithToken, resultUsers, resultOk, resultErr = []string{}, nil, false, nil
a.AuthenticateToken(context.Background(), "bad1") a.AuthenticateToken(context.Background(), "bad1")
@ -127,10 +124,7 @@ func TestCachedTokenAuthenticatorWithAudiences(t *testing.T) {
}) })
fakeClock := utilclock.NewFakeClock(time.Now()) fakeClock := utilclock.NewFakeClock(time.Now())
ctx, cancel := context.WithCancel(context.Background()) a := newWithClock(fakeAuth, true, time.Minute, 0, fakeClock)
defer cancel()
a := newWithClock(ctx, fakeAuth, true, time.Minute, 0, fakeClock)
resultUsers["audAusertoken1"] = &user.DefaultInfo{Name: "user1"} resultUsers["audAusertoken1"] = &user.DefaultInfo{Name: "user1"}
resultUsers["audBusertoken1"] = &user.DefaultInfo{Name: "user1-different"} resultUsers["audBusertoken1"] = &user.DefaultInfo{Name: "user1-different"}
@ -276,8 +270,6 @@ func (s *singleBenchmark) run(b *testing.B) {
} }
func (s *singleBenchmark) bench(b *testing.B) { func (s *singleBenchmark) bench(b *testing.B) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Simulate slowness, qps limit, external service limitation, etc // Simulate slowness, qps limit, external service limitation, etc
const maxInFlight = 40 const maxInFlight = 40
chokepoint := make(chan struct{}, maxInFlight) chokepoint := make(chan struct{}, maxInFlight)
@ -285,7 +277,6 @@ func (s *singleBenchmark) bench(b *testing.B) {
var lookups uint64 var lookups uint64
a := newWithClock( a := newWithClock(
ctx,
authenticator.TokenFunc(func(ctx context.Context, token string) (*authenticator.Response, bool, error) { authenticator.TokenFunc(func(ctx context.Context, token string) (*authenticator.Response, bool, error) {
atomic.AddUint64(&lookups, 1) atomic.AddUint64(&lookups, 1)

View File

@ -170,7 +170,7 @@ func (m *mockV1Service) HTTPStatusCode() int { return m.statusCode }
// newV1TokenAuthenticator creates a temporary kubeconfig file from the provided // newV1TokenAuthenticator creates a temporary kubeconfig file from the provided
// arguments and attempts to load a new WebhookTokenAuthenticator from it. // arguments and attempts to load a new WebhookTokenAuthenticator from it.
func newV1TokenAuthenticator(ctx context.Context, serverURL string, clientCert, clientKey, ca []byte, cacheTime time.Duration, implicitAuds authenticator.Audiences) (authenticator.Token, error) { func newV1TokenAuthenticator(serverURL string, clientCert, clientKey, ca []byte, cacheTime time.Duration, implicitAuds authenticator.Audiences) (authenticator.Token, error) {
tempfile, err := ioutil.TempFile("", "") tempfile, err := ioutil.TempFile("", "")
if err != nil { if err != nil {
return nil, err return nil, err
@ -203,7 +203,7 @@ func newV1TokenAuthenticator(ctx context.Context, serverURL string, clientCert,
return nil, err return nil, err
} }
return cache.New(ctx, authn, false, cacheTime, cacheTime), nil return cache.New(authn, false, cacheTime, cacheTime), nil
} }
func TestV1TLSConfig(t *testing.T) { func TestV1TLSConfig(t *testing.T) {
@ -259,10 +259,7 @@ func TestV1TLSConfig(t *testing.T) {
} }
defer server.Close() defer server.Close()
ctx, cancel := context.WithCancel(context.Background()) wh, err := newV1TokenAuthenticator(server.URL, tt.clientCert, tt.clientKey, tt.clientCA, 0, nil)
defer cancel()
wh, err := newV1TokenAuthenticator(ctx, server.URL, tt.clientCert, tt.clientKey, tt.clientCA, 0, nil)
if err != nil { if err != nil {
t.Errorf("%s: failed to create client: %v", tt.test, err) t.Errorf("%s: failed to create client: %v", tt.test, err)
return return
@ -485,14 +482,12 @@ func TestV1WebhookTokenAuthenticator(t *testing.T) {
token := "my-s3cr3t-t0ken" token := "my-s3cr3t-t0ken"
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) { t.Run(tt.description, func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) wh, err := newV1TokenAuthenticator(s.URL, clientCert, clientKey, caCert, 0, tt.implicitAuds)
defer cancel()
wh, err := newV1TokenAuthenticator(ctx, s.URL, clientCert, clientKey, caCert, 0, tt.implicitAuds)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
ctx := context.Background()
if tt.reqAuds != nil { if tt.reqAuds != nil {
ctx = authenticator.WithAudiences(ctx, tt.reqAuds) ctx = authenticator.WithAudiences(ctx, tt.reqAuds)
} }
@ -559,11 +554,8 @@ func TestV1WebhookCacheAndRetry(t *testing.T) {
} }
defer s.Close() defer s.Close()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Create an authenticator that caches successful responses "forever" (100 days). // Create an authenticator that caches successful responses "forever" (100 days).
wh, err := newV1TokenAuthenticator(ctx, s.URL, clientCert, clientKey, caCert, 2400*time.Hour, nil) wh, err := newV1TokenAuthenticator(s.URL, clientCert, clientKey, caCert, 2400*time.Hour, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -172,7 +172,7 @@ func (m *mockV1beta1Service) HTTPStatusCode() int { return m.statusCode }
// newV1beta1TokenAuthenticator creates a temporary kubeconfig file from the provided // newV1beta1TokenAuthenticator creates a temporary kubeconfig file from the provided
// arguments and attempts to load a new WebhookTokenAuthenticator from it. // arguments and attempts to load a new WebhookTokenAuthenticator from it.
func newV1beta1TokenAuthenticator(ctx context.Context, serverURL string, clientCert, clientKey, ca []byte, cacheTime time.Duration, implicitAuds authenticator.Audiences) (authenticator.Token, error) { func newV1beta1TokenAuthenticator(serverURL string, clientCert, clientKey, ca []byte, cacheTime time.Duration, implicitAuds authenticator.Audiences) (authenticator.Token, error) {
tempfile, err := ioutil.TempFile("", "") tempfile, err := ioutil.TempFile("", "")
if err != nil { if err != nil {
return nil, err return nil, err
@ -205,7 +205,7 @@ func newV1beta1TokenAuthenticator(ctx context.Context, serverURL string, clientC
return nil, err return nil, err
} }
return cache.New(ctx, authn, false, cacheTime, cacheTime), nil return cache.New(authn, false, cacheTime, cacheTime), nil
} }
func TestV1beta1TLSConfig(t *testing.T) { func TestV1beta1TLSConfig(t *testing.T) {
@ -261,10 +261,7 @@ func TestV1beta1TLSConfig(t *testing.T) {
} }
defer server.Close() defer server.Close()
ctx, cancel := context.WithCancel(context.Background()) wh, err := newV1beta1TokenAuthenticator(server.URL, tt.clientCert, tt.clientKey, tt.clientCA, 0, nil)
defer cancel()
wh, err := newV1beta1TokenAuthenticator(ctx, server.URL, tt.clientCert, tt.clientKey, tt.clientCA, 0, nil)
if err != nil { if err != nil {
t.Errorf("%s: failed to create client: %v", tt.test, err) t.Errorf("%s: failed to create client: %v", tt.test, err)
return return
@ -487,14 +484,12 @@ func TestV1beta1WebhookTokenAuthenticator(t *testing.T) {
token := "my-s3cr3t-t0ken" token := "my-s3cr3t-t0ken"
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) { t.Run(tt.description, func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) wh, err := newV1beta1TokenAuthenticator(s.URL, clientCert, clientKey, caCert, 0, tt.implicitAuds)
defer cancel()
wh, err := newV1beta1TokenAuthenticator(ctx, s.URL, clientCert, clientKey, caCert, 0, tt.implicitAuds)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
ctx := context.Background()
if tt.reqAuds != nil { if tt.reqAuds != nil {
ctx = authenticator.WithAudiences(ctx, tt.reqAuds) ctx = authenticator.WithAudiences(ctx, tt.reqAuds)
} }
@ -561,11 +556,8 @@ func TestV1beta1WebhookCacheAndRetry(t *testing.T) {
} }
defer s.Close() defer s.Close()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Create an authenticator that caches successful responses "forever" (100 days). // Create an authenticator that caches successful responses "forever" (100 days).
wh, err := newV1beta1TokenAuthenticator(ctx, s.URL, clientCert, clientKey, caCert, 2400*time.Hour, nil) wh, err := newV1beta1TokenAuthenticator(s.URL, clientCert, clientKey, caCert, 2400*time.Hour, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -70,7 +70,7 @@ func getTestTokenAuth() authenticator.Request {
return group.NewGroupAdder(bearertoken.New(tokenAuthenticator), []string{user.AllAuthenticated}) return group.NewGroupAdder(bearertoken.New(tokenAuthenticator), []string{user.AllAuthenticated})
} }
func getTestWebhookTokenAuth(ctx context.Context, serverURL string) (authenticator.Request, error) { func getTestWebhookTokenAuth(serverURL string) (authenticator.Request, error) {
kubecfgFile, err := ioutil.TempFile("", "webhook-kubecfg") kubecfgFile, err := ioutil.TempFile("", "webhook-kubecfg")
if err != nil { if err != nil {
return nil, err return nil, err
@ -90,7 +90,7 @@ func getTestWebhookTokenAuth(ctx context.Context, serverURL string) (authenticat
if err != nil { if err != nil {
return nil, err return nil, err
} }
return bearertoken.New(cache.New(ctx, webhookTokenAuth, false, 2*time.Minute, 2*time.Minute)), nil return bearertoken.New(cache.New(webhookTokenAuth, false, 2*time.Minute, 2*time.Minute)), nil
} }
func path(resource, namespace, name string) string { func path(resource, namespace, name string) string {
@ -1176,11 +1176,7 @@ func TestReadOnlyAuthorization(t *testing.T) {
func TestWebhookTokenAuthenticator(t *testing.T) { func TestWebhookTokenAuthenticator(t *testing.T) {
authServer := newTestWebhookTokenAuthServer() authServer := newTestWebhookTokenAuthServer()
defer authServer.Close() defer authServer.Close()
authenticator, err := getTestWebhookTokenAuth(authServer.URL)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
authenticator, err := getTestWebhookTokenAuth(ctx, authServer.URL)
if err != nil { if err != nil {
t.Fatalf("error starting webhook token authenticator server: %v", err) t.Fatalf("error starting webhook token authenticator server: %v", err)
} }