diff --git a/.changelog/16085.txt b/.changelog/16085.txt new file mode 100644 index 000000000..92a9a8528 --- /dev/null +++ b/.changelog/16085.txt @@ -0,0 +1,3 @@ +```release-note:improvement +acl: refactor ACL cache based on golang-lru/v2 +``` diff --git a/client/acl.go b/client/acl.go index 19efb6cd0..da76b1c1d 100644 --- a/client/acl.go +++ b/client/acl.go @@ -3,8 +3,7 @@ package client import ( "time" - metrics "github.com/armon/go-metrics" - lru "github.com/hashicorp/golang-lru" + "github.com/armon/go-metrics" "github.com/hashicorp/nomad/acl" "github.com/hashicorp/nomad/nomad/structs" ) @@ -32,54 +31,25 @@ const ( // of ACLs type clientACLResolver struct { // aclCache is used to maintain the parsed ACL objects - aclCache *lru.TwoQueueCache + aclCache *structs.ACLCache[*acl.ACL] // policyCache is used to maintain the fetched policy objects - policyCache *lru.TwoQueueCache + policyCache *structs.ACLCache[*structs.ACLPolicy] // tokenCache is used to maintain the fetched token objects - tokenCache *lru.TwoQueueCache + tokenCache *structs.ACLCache[*structs.ACLToken] // roleCache is used to maintain a cache of the fetched ACL roles. Each // entry is keyed by the role ID. - roleCache *lru.TwoQueueCache + roleCache *structs.ACLCache[*structs.ACLRole] } // init is used to setup the client resolver state -func (c *clientACLResolver) init() error { - // Create the ACL object cache - var err error - c.aclCache, err = lru.New2Q(aclCacheSize) - if err != nil { - return err - } - c.policyCache, err = lru.New2Q(policyCacheSize) - if err != nil { - return err - } - c.tokenCache, err = lru.New2Q(tokenCacheSize) - if err != nil { - return err - } - c.roleCache, err = lru.New2Q(roleCacheSize) - if err != nil { - return err - } - return nil -} - -// cachedACLValue is used to manage ACL Token, Policy, or Role cache entries -// and their TTLs. -type cachedACLValue struct { - Token *structs.ACLToken - Policy *structs.ACLPolicy - Role *structs.ACLRole - CacheTime time.Time -} - -// Age is the time since the token was cached -func (c *cachedACLValue) Age() time.Duration { - return time.Since(c.CacheTime) +func (c *clientACLResolver) init() { + c.aclCache = structs.NewACLCache[*acl.ACL](aclCacheSize) + c.policyCache = structs.NewACLCache[*structs.ACLPolicy](policyCacheSize) + c.tokenCache = structs.NewACLCache[*structs.ACLToken](tokenCacheSize) + c.roleCache = structs.NewACLCache[*structs.ACLRole](roleCacheSize) } // ResolveToken is used to translate an ACL Token Secret ID into @@ -154,12 +124,11 @@ func (c *Client) resolveTokenValue(secretID string) (*structs.ACLToken, error) { return structs.AnonymousACLToken, nil } - // Lookup the token in the cache - raw, ok := c.tokenCache.Get(secretID) + // Lookup the token entry in the cache + entry, ok := c.tokenCache.Get(secretID) if ok { - cached := raw.(*cachedACLValue) - if cached.Age() <= c.GetConfig().ACLTokenTTL { - return cached.Token, nil + if entry.Age() <= c.GetConfig().ACLTokenTTL { + return entry.Get(), nil } } @@ -176,17 +145,13 @@ func (c *Client) resolveTokenValue(secretID string) (*structs.ACLToken, error) { // If we encounter an error but have a cached value, mask the error and extend the cache if ok { c.logger.Warn("failed to resolve token, using expired cached value", "error", err) - cached := raw.(*cachedACLValue) - return cached.Token, nil + return entry.Get(), nil } return nil, err } // Cache the response (positive or negative) - c.tokenCache.Add(secretID, &cachedACLValue{ - Token: resp.Token, - CacheTime: time.Now(), - }) + c.tokenCache.Add(secretID, resp.Token) return resp.Token, nil } @@ -202,18 +167,17 @@ func (c *Client) resolvePolicies(secretID string, policies []string) ([]*structs // Scan the cache for each policy for _, policyName := range policies { // Lookup the policy in the cache - raw, ok := c.policyCache.Get(policyName) + entry, ok := c.policyCache.Get(policyName) if !ok { missing = append(missing, policyName) continue } // Check if the cached value is valid or expired - cached := raw.(*cachedACLValue) - if cached.Age() <= c.GetConfig().ACLPolicyTTL { - out = append(out, cached.Policy) + if entry.Age() <= c.GetConfig().ACLPolicyTTL { + out = append(out, entry.Get()) } else { - expired = append(expired, cached.Policy) + expired = append(expired, entry.Get()) } } @@ -248,10 +212,7 @@ func (c *Client) resolvePolicies(secretID string, policies []string) ([]*structs // Handle each output for _, policy := range resp.Policies { - c.policyCache.Add(policy.Name, &cachedACLValue{ - Policy: policy, - CacheTime: time.Now(), - }) + c.policyCache.Add(policy.Name, policy) out = append(out, policy) } @@ -290,7 +251,7 @@ func (c *Client) resolveTokenACLRoles(secretID string, roleLinks []*structs.ACLT // Look within the cache to see if the role is already present. If we // do not find it, add the ID to our tracking, so we look this up via // RPC. - raw, ok := c.roleCache.Get(roleLink.ID) + entry, ok := c.roleCache.Get(roleLink.ID) if !ok { missingRoleIDs = append(missingRoleIDs, roleLink.ID) continue @@ -299,13 +260,12 @@ func (c *Client) resolveTokenACLRoles(secretID string, roleLinks []*structs.ACLT // If the cached value is expired, add the ID to our tracking, so we // look this up via RPC. Otherwise, iterate the policy links and add // each policy name to our return object tracking. - cached := raw.(*cachedACLValue) - if cached.Age() <= c.GetConfig().ACLRoleTTL { - for _, policyLink := range cached.Role.Policies { + if entry.Age() <= c.GetConfig().ACLRoleTTL { + for _, policyLink := range entry.Get().Policies { policyNames = append(policyNames, policyLink.Name) } } else { - expiredRoleIDs = append(expiredRoleIDs, cached.Role.ID) + expiredRoleIDs = append(expiredRoleIDs, entry.Get().ID) } } @@ -354,13 +314,11 @@ func (c *Client) resolveTokenACLRoles(secretID string, roleLinks []*structs.ACLT // Generate a timestamp for the cache entry. We do not need to use a // timestamp per ACL role response integration. now := time.Now() - for _, aclRole := range roleByIDResp.ACLRoles { - // Add an entry to the cache using the generated timestamp for future // expiry calculations. Any existing, expired entry will be // overwritten. - c.roleCache.Add(aclRole.ID, &cachedACLValue{Role: aclRole, CacheTime: now}) + c.roleCache.AddAtTime(aclRole.ID, aclRole, now) // Iterate the role policy links, extracting the name and adding this // to our return response tracking. diff --git a/client/acl_test.go b/client/acl_test.go index 7a108b8db..bb83038ad 100644 --- a/client/acl_test.go +++ b/client/acl_test.go @@ -17,8 +17,8 @@ import ( ) func Test_clientACLResolver_init(t *testing.T) { - resolver := &clientACLResolver{} - must.NoError(t, resolver.init()) + resolver := new(clientACLResolver) + resolver.init() must.NotNil(t, resolver.aclCache) must.NotNil(t, resolver.policyCache) must.NotNil(t, resolver.tokenCache) diff --git a/client/client.go b/client/client.go index 5c3f25ba7..da1390501 100644 --- a/client/client.go +++ b/client/client.go @@ -441,9 +441,7 @@ func NewClient(cfg *config.Config, consulCatalog consul.CatalogAPI, consulProxie c.setupClientRpc(rpcs) // Initialize the ACL state - if err := c.clientACLResolver.init(); err != nil { - return nil, fmt.Errorf("failed to initialize ACL state: %v", err) - } + c.clientACLResolver.init() // Setup the node if err := c.setupNode(); err != nil { diff --git a/go.mod b/go.mod index a91301de1..5a1e3d58e 100644 --- a/go.mod +++ b/go.mod @@ -69,7 +69,7 @@ require ( github.com/hashicorp/go-syslog v1.0.0 github.com/hashicorp/go-uuid v1.0.3 github.com/hashicorp/go-version v1.6.0 - github.com/hashicorp/golang-lru v0.5.4 + github.com/hashicorp/golang-lru/v2 v2.0.1 github.com/hashicorp/hcl v1.0.1-vault-3 github.com/hashicorp/hcl/v2 v2.9.2-0.20220525143345-ab3cae0737bc github.com/hashicorp/hil v0.0.0-20210521165536-27a72121fd40 @@ -214,7 +214,7 @@ require ( github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1 // indirect github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.2 // indirect - github.com/hashicorp/golang-lru/v2 v2.0.0 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/mdns v1.0.4 // indirect github.com/hashicorp/vault/api/auth/kubernetes v0.3.0 // indirect github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443 // indirect diff --git a/go.sum b/go.sum index 64b9f1af3..2320f87df 100644 --- a/go.sum +++ b/go.sum @@ -732,8 +732,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/golang-lru/v2 v2.0.0 h1:Lf+9eD8m5pncvHAOCQj49GSN6aQI8XGfI5OpXNkoWaA= -github.com/hashicorp/golang-lru/v2 v2.0.0/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/golang-lru/v2 v2.0.1 h1:5pv5N1lT1fjLg2VQ5KWc7kmucp2x/kvFOnxuVTqZ6x4= +github.com/hashicorp/golang-lru/v2 v2.0.1/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.1-0.20201016140508-a07e7d50bbee h1:8B4HqvMUtYSjsGkYjiQGStc9pXffY2J+Z2SPQAj+wMY= github.com/hashicorp/hcl v1.0.1-0.20201016140508-a07e7d50bbee/go.mod h1:gwlu9+/P9MmKtYrMsHeFRZPXj2CTPm11TDnMeaRHS7g= github.com/hashicorp/hcl/v2 v2.9.2-0.20220525143345-ab3cae0737bc h1:32lGaCPq5JPYNgFFTjl/cTIar9UWWxCbimCs5G2hMHg= diff --git a/lib/lang/doc.go b/lib/lang/doc.go new file mode 100644 index 000000000..04b5f7c88 --- /dev/null +++ b/lib/lang/doc.go @@ -0,0 +1,2 @@ +// Package lang provides some features that really 'ought to be part of the Go language +package lang diff --git a/lib/lang/pair.go b/lib/lang/pair.go new file mode 100644 index 000000000..2b1e6b58a --- /dev/null +++ b/lib/lang/pair.go @@ -0,0 +1,7 @@ +package lang + +// Pair associates two arbitrary types together. +type Pair[T, U any] struct { + First T + Second U +} diff --git a/nomad/acl.go b/nomad/acl.go index f658a7b54..4327f7403 100644 --- a/nomad/acl.go +++ b/nomad/acl.go @@ -7,7 +7,6 @@ import ( "time" metrics "github.com/armon/go-metrics" - lru "github.com/hashicorp/golang-lru" "github.com/hashicorp/nomad/acl" "github.com/hashicorp/nomad/helper" "github.com/hashicorp/nomad/nomad/state" @@ -283,7 +282,7 @@ func (s *Server) ResolveClaims(claims *structs.IdentityClaims) (*acl.ACL, error) // resolveTokenFromSnapshotCache is used to resolve an ACL object from a // snapshot of state, using a cache to avoid parsing and ACL construction when // possible. It is split from resolveToken to simplify testing. -func resolveTokenFromSnapshotCache(snap *state.StateSnapshot, cache *lru.TwoQueueCache, secretID string) (*acl.ACL, error) { +func resolveTokenFromSnapshotCache(snap *state.StateSnapshot, cache *structs.ACLCache[*acl.ACL], secretID string) (*acl.ACL, error) { // Lookup the ACL Token var token *structs.ACLToken var err error @@ -308,7 +307,7 @@ func resolveTokenFromSnapshotCache(snap *state.StateSnapshot, cache *lru.TwoQueu } -func resolveACLFromToken(snap *state.StateSnapshot, cache *lru.TwoQueueCache, token *structs.ACLToken) (*acl.ACL, error) { +func resolveACLFromToken(snap *state.StateSnapshot, cache *structs.ACLCache[*acl.ACL], token *structs.ACLToken) (*acl.ACL, error) { // Check if this is a management token if token.Type == structs.ACLManagementToken { diff --git a/nomad/plan_apply_node_tracker.go b/nomad/plan_apply_node_tracker.go index 48783fbc4..6e964cde0 100644 --- a/nomad/plan_apply_node_tracker.go +++ b/nomad/plan_apply_node_tracker.go @@ -4,9 +4,9 @@ import ( "fmt" "time" - metrics "github.com/armon/go-metrics" + "github.com/armon/go-metrics" "github.com/hashicorp/go-hclog" - lru "github.com/hashicorp/golang-lru" + lru "github.com/hashicorp/golang-lru/v2" "github.com/hashicorp/nomad/helper" "golang.org/x/time/rate" ) @@ -36,7 +36,7 @@ func (n *NoopBadNodeTracker) Add(string) bool { // frequency and recency. type CachedBadNodeTracker struct { logger hclog.Logger - cache *lru.TwoQueueCache + cache *lru.TwoQueueCache[string, *badNodeStats] limiter *rate.Limiter window time.Duration threshold int @@ -72,7 +72,7 @@ func NewCachedBadNodeTracker(logger hclog.Logger, config CachedBadNodeTrackerCon With("window", config.Window). With("threshold", config.Threshold) - cache, err := lru.New2Q(config.CacheSize) + cache, err := lru.New2Q[string, *badNodeStats](config.CacheSize) if err != nil { return nil, fmt.Errorf("failed to create new bad node tracker: %v", err) } @@ -93,12 +93,11 @@ func NewCachedBadNodeTracker(logger hclog.Logger, config CachedBadNodeTrackerCon // cache. If the cache is full the least recently updated or accessed node is // evicted. func (c *CachedBadNodeTracker) Add(nodeID string) bool { - value, ok := c.cache.Get(nodeID) + stats, ok := c.cache.Get(nodeID) if !ok { - value = newBadNodeStats(nodeID, c.window) - c.cache.Add(nodeID, value) + stats = newBadNodeStats(nodeID, c.window) + c.cache.Add(nodeID, stats) } - stats := value.(*badNodeStats) now := time.Now() stats.record(now) @@ -147,13 +146,12 @@ func (c *CachedBadNodeTracker) isBad(t time.Time, stats *badNodeStats) bool { func (c *CachedBadNodeTracker) emitStats() { now := time.Now() - for _, k := range c.cache.Keys() { - value, _ := c.cache.Get(k) - stats := value.(*badNodeStats) + for _, nodeID := range c.cache.Keys() { + stats, _ := c.cache.Get(nodeID) score := stats.score(now) labels := []metrics.Label{ - {Name: "node_id", Value: k.(string)}, + {Name: "node_id", Value: nodeID}, } metrics.SetGaugeWithLabels([]string{"nomad", "plan", "rejection_tracker", "node_score"}, float32(score), labels) } diff --git a/nomad/plan_apply_node_tracker_test.go b/nomad/plan_apply_node_tracker_test.go index 018335c4f..6a1704883 100644 --- a/nomad/plan_apply_node_tracker_test.go +++ b/nomad/plan_apply_node_tracker_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/require" ) -func TesCachedtBadNodeTracker(t *testing.T) { +func TestCachedtBadNodeTracker(t *testing.T) { ci.Parallel(t) config := DefaultCachedBadNodeTrackerConfig() @@ -74,11 +74,10 @@ func TestCachedBadNodeTracker_isBad(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Read value from cached. - v, ok := tracker.cache.Get(tc.nodeID) + stats, ok := tracker.cache.Get(tc.nodeID) require.True(t, ok) // Check if it's bad. - stats := v.(*badNodeStats) got := tracker.isBad(now, stats) require.Equal(t, tc.bad, got) }) @@ -88,10 +87,9 @@ func TestCachedBadNodeTracker_isBad(t *testing.T) { nodes := []string{"node-1", "node-2", "node-3"} for _, n := range nodes { t.Run(fmt.Sprintf("%s cache expires", n), func(t *testing.T) { - v, ok := tracker.cache.Get(n) + stats, ok := tracker.cache.Get(n) require.True(t, ok) - stats := v.(*badNodeStats) bad := tracker.isBad(future, stats) require.False(t, bad) }) @@ -115,11 +113,9 @@ func TesCachedtBadNodeTracker_rateLimit(t *testing.T) { tracker.Add("node-1") tracker.Add("node-1") - v, ok := tracker.cache.Get("node-1") + stats, ok := tracker.cache.Get("node-1") require.True(t, ok) - stats := v.(*badNodeStats) - // Burst allows for max 3 operations. now := time.Now() require.True(t, tracker.isBad(now, stats)) diff --git a/nomad/server.go b/nomad/server.go index 725202c4b..5d8e96ef3 100644 --- a/nomad/server.go +++ b/nomad/server.go @@ -23,7 +23,7 @@ import ( consulapi "github.com/hashicorp/consul/api" log "github.com/hashicorp/go-hclog" multierror "github.com/hashicorp/go-multierror" - lru "github.com/hashicorp/golang-lru" + "github.com/hashicorp/nomad/acl" "github.com/hashicorp/raft" autopilot "github.com/hashicorp/raft-autopilot" raftboltdb "github.com/hashicorp/raft-boltdb/v2" @@ -254,7 +254,7 @@ type Server struct { workersEventCh chan interface{} // aclCache is used to maintain the parsed ACL objects - aclCache *lru.TwoQueueCache + aclCache *structs.ACLCache[*acl.ACL] // oidcProviderCache maintains a cache of OIDC providers. This is useful as // the provider performs background HTTP requests. When the Nomad server is @@ -341,10 +341,7 @@ func NewServer(config *Config, consulCatalog consul.CatalogAPI, consulConfigEntr } // Create the ACL object cache - aclCache, err := lru.New2Q(aclCacheSize) - if err != nil { - return nil, err - } + aclCache := structs.NewACLCache[*acl.ACL](aclCacheSize) // Create the logger logger := config.Logger.ResetNamedIntercept("nomad") diff --git a/nomad/stream/event_broker.go b/nomad/stream/event_broker.go index 8ecf33ebd..032a22020 100644 --- a/nomad/stream/event_broker.go +++ b/nomad/stream/event_broker.go @@ -10,7 +10,6 @@ import ( "github.com/armon/go-metrics" "github.com/hashicorp/go-memdb" - lru "github.com/hashicorp/golang-lru" "github.com/hashicorp/nomad/acl" "github.com/hashicorp/nomad/nomad/structs" @@ -42,7 +41,7 @@ type EventBroker struct { publishCh chan *structs.Events aclDelegate ACLDelegate - aclCache *lru.TwoQueueCache + aclCache *structs.ACLCache[*acl.ACL] aclCh chan structs.Event @@ -63,11 +62,6 @@ func NewEventBroker(ctx context.Context, aclDelegate ACLDelegate, cfg EventBroke cfg.EventBufferSize = 100 } - aclCache, err := lru.New2Q(aclCacheSize) - if err != nil { - return nil, err - } - buffer := newEventBuffer(cfg.EventBufferSize) e := &EventBroker{ logger: cfg.Logger.Named("event_broker"), @@ -75,7 +69,7 @@ func NewEventBroker(ctx context.Context, aclDelegate ACLDelegate, cfg EventBroke publishCh: make(chan *structs.Events, 64), aclCh: make(chan structs.Event, 10), aclDelegate: aclDelegate, - aclCache: aclCache, + aclCache: structs.NewACLCache[*acl.ACL](aclCacheSize), subscriptions: &subscriptions{ byToken: make(map[string]map[*SubscribeRequest]*Subscription), }, @@ -277,7 +271,7 @@ func (e *EventBroker) checkSubscriptionsAgainstACLChange() { } func aclObjFromSnapshotForTokenSecretID( - aclSnapshot ACLTokenProvider, aclCache *lru.TwoQueueCache, tokenSecretID string) ( + aclSnapshot ACLTokenProvider, aclCache *structs.ACLCache[*acl.ACL], tokenSecretID string) ( *acl.ACL, *time.Time, error) { aclToken, err := aclSnapshot.ACLTokenBySecretID(nil, tokenSecretID) diff --git a/nomad/structs/acl.go b/nomad/structs/acl.go index 158fa81eb..d440b051f 100644 --- a/nomad/structs/acl.go +++ b/nomad/structs/acl.go @@ -11,11 +11,14 @@ import ( "github.com/hashicorp/go-multierror" "github.com/hashicorp/go-set" + lru "github.com/hashicorp/golang-lru/v2" "github.com/hashicorp/nomad/helper" "github.com/hashicorp/nomad/helper/pointer" "github.com/hashicorp/nomad/helper/uuid" + "github.com/hashicorp/nomad/lib/lang" "golang.org/x/crypto/blake2b" "golang.org/x/exp/slices" + "oss.indeed.com/go/libtime" ) const ( @@ -187,10 +190,48 @@ var ( // ValidACLRoleName is used to validate an ACL role name. ValidACLRoleName = regexp.MustCompile("^[a-zA-Z0-9-]{1,128}$") - // validACLAuthMethodName is used to validate an ACL auth method name. + // ValidACLAuthMethod is used to validate an ACL auth method name. ValidACLAuthMethod = regexp.MustCompile("^[a-zA-Z0-9-]{1,128}$") ) +type ACLCacheEntry[T any] lang.Pair[T, time.Time] + +func (e ACLCacheEntry[T]) Age() time.Duration { + return time.Since(e.Second) +} + +func (e ACLCacheEntry[T]) Get() T { + return e.First +} + +// An ACLCache caches ACL tokens by their policy content. +type ACLCache[T any] struct { + *lru.TwoQueueCache[string, ACLCacheEntry[T]] + clock libtime.Clock +} + +func (c *ACLCache[T]) Add(key string, item T) { + c.AddAtTime(key, item, c.clock.Now()) +} + +func (c *ACLCache[T]) AddAtTime(key string, item T, now time.Time) { + c.TwoQueueCache.Add(key, ACLCacheEntry[T]{ + First: item, + Second: now, + }) +} + +func NewACLCache[T any](size int) *ACLCache[T] { + c, err := lru.New2Q[string, ACLCacheEntry[T]](size) + if err != nil { + panic(err) // not possible + } + return &ACLCache[T]{ + TwoQueueCache: c, + clock: libtime.SystemClock(), + } +} + // ACLTokenRoleLink is used to link an ACL token to an ACL role. The ACL token // can therefore inherit all the ACL policy permissions that the ACL role // contains. diff --git a/nomad/structs/funcs.go b/nomad/structs/funcs.go index b3a114072..89c6d84b2 100644 --- a/nomad/structs/funcs.go +++ b/nomad/structs/funcs.go @@ -10,9 +10,8 @@ import ( "strconv" "strings" - multierror "github.com/hashicorp/go-multierror" + "github.com/hashicorp/go-multierror" "github.com/hashicorp/go-set" - lru "github.com/hashicorp/golang-lru" "github.com/hashicorp/nomad/acl" "golang.org/x/crypto/blake2b" ) @@ -436,7 +435,7 @@ func ACLPolicyListHash(policies []*ACLPolicy) string { } // CompileACLObject compiles a set of ACL policies into an ACL object with a cache -func CompileACLObject(cache *lru.TwoQueueCache, policies []*ACLPolicy) (*acl.ACL, error) { +func CompileACLObject(cache *ACLCache[*acl.ACL], policies []*ACLPolicy) (*acl.ACL, error) { // Sort the policies to ensure consistent ordering sort.Slice(policies, func(i, j int) bool { return policies[i].Name < policies[j].Name @@ -444,9 +443,9 @@ func CompileACLObject(cache *lru.TwoQueueCache, policies []*ACLPolicy) (*acl.ACL // Determine the cache key cacheKey := ACLPolicyListHash(policies) - aclRaw, ok := cache.Get(cacheKey) + entry, ok := cache.Get(cacheKey) if ok { - return aclRaw.(*acl.ACL), nil + return entry.Get(), nil } // Parse the policies diff --git a/nomad/structs/funcs_test.go b/nomad/structs/funcs_test.go index 9f9f26af3..ec4cbe139 100644 --- a/nomad/structs/funcs_test.go +++ b/nomad/structs/funcs_test.go @@ -6,7 +6,7 @@ import ( "fmt" "testing" - lru "github.com/hashicorp/golang-lru" + "github.com/hashicorp/nomad/acl" "github.com/hashicorp/nomad/ci" "github.com/hashicorp/nomad/helper/uuid" "github.com/stretchr/testify/assert" @@ -1007,8 +1007,7 @@ func TestCompileACLObject(t *testing.T) { p2.Name = fmt.Sprintf("policy-%s", uuid.Generate()) // Create a small cache - cache, err := lru.New2Q(16) - assert.Nil(t, err) + cache := NewACLCache[*acl.ACL](10) // Test compilation aclObj, err := CompileACLObject(cache, []*ACLPolicy{p1}) diff --git a/nomad/structs/generate.sh b/nomad/structs/generate.sh index 024e55a75..8e253fd92 100755 --- a/nomad/structs/generate.sh +++ b/nomad/structs/generate.sh @@ -8,5 +8,5 @@ codecgen \ -d 100 \ -t codegen_generated \ -o structs.generated.go \ - -nr="^IdentityClaims$" \ + -nr="(^ACLCache$)|(^IdentityClaims$)" \ ${FILES}