consul: split ACL cache into a reusable struct

This commit is contained in:
Ryan Uber 2015-06-18 16:18:10 -07:00
parent 713f1781c8
commit e04a23801e
1 changed files with 58 additions and 12 deletions

View File

@ -11,6 +11,7 @@ import (
"github.com/armon/go-metrics"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/consul/structs"
"github.com/hashicorp/golang-lru"
)
const (
@ -30,6 +31,9 @@ const (
// anonymousToken is the token ID we re-write to if there
// is no token ID provided
anonymousToken = "anonymous"
// Maximum number of cached ACL entries
aclCacheSize = 256
)
var (
@ -89,15 +93,57 @@ func (s *Server) resolveToken(id string) (acl.ACL, error) {
}
// Use our non-authoritative cache
return s.lookupACL(id, authDC)
return s.aclCache.lookupACL(id, authDC)
}
// rpcFn is used to make an RPC call to the client or server.
type rpcFn func(string, interface{}, interface{}) error
// aclCache is used to cache ACL's and policies.
type aclCache struct {
config *Config
logger *log.Logger
// acls is a non-authoritative ACL cache
acls *lru.Cache
// aclPolicyCache is a policy cache
policies *lru.Cache
// The RPC function used to talk to the client/server
rpc rpcFn
}
// newAclCache returns a new cache layer for ACLs and policies
func newAclCache(conf *Config, logger *log.Logger, rpc rpcFn) (*aclCache, error) {
var err error
cache := &aclCache{
config: conf,
logger: logger,
rpc: rpc,
}
// Initialize the non-authoritative ACL cache
cache.acls, err = lru.New(aclCacheSize)
if err != nil {
return nil, fmt.Errorf("Failed to create ACL cache: %v", err)
}
// Initialize the ACL policy cache
cache.policies, err = lru.New(aclCacheSize)
if err != nil {
return nil, fmt.Errorf("Failed to create ACL policy cache: %v", err)
}
return cache, nil
}
// lookupACL is used when we are non-authoritative, and need
// to resolve an ACL
func (s *Server) lookupACL(id, authDC string) (acl.ACL, error) {
func (c *aclCache) lookupACL(id, authDC string) (acl.ACL, error) {
// Check the cache for the ACL
var cached *aclCacheEntry
raw, ok := s.aclCache.Get(id)
raw, ok := c.acls.Get(id)
if ok {
cached = raw.(*aclCacheEntry)
}
@ -119,22 +165,22 @@ func (s *Server) lookupACL(id, authDC string) (acl.ACL, error) {
args.ETag = cached.ETag
}
var out structs.ACLPolicy
err := s.RPC("ACL.GetPolicy", &args, &out)
err := c.rpc("ACL.GetPolicy", &args, &out)
// Handle the happy path
if err == nil {
return s.useACLPolicy(id, authDC, cached, &out)
return c.useACLPolicy(id, authDC, cached, &out)
}
// Check for not-found
if strings.Contains(err.Error(), aclNotFound) {
return nil, errors.New(aclNotFound)
} else {
s.logger.Printf("[ERR] consul.acl: Failed to get policy for '%s': %v", id, err)
c.logger.Printf("[ERR] consul.acl: Failed to get policy for '%s': %v", id, err)
}
// Unable to refresh, apply the down policy
switch s.config.ACLDownPolicy {
switch c.config.ACLDownPolicy {
case "allow":
return acl.AllowAll(), nil
case "extend-cache":
@ -148,7 +194,7 @@ func (s *Server) lookupACL(id, authDC string) (acl.ACL, error) {
}
// useACLPolicy handles an ACLPolicy response
func (s *Server) useACLPolicy(id, authDC string, cached *aclCacheEntry, p *structs.ACLPolicy) (acl.ACL, error) {
func (c *aclCache) useACLPolicy(id, authDC string, cached *aclCacheEntry, p *structs.ACLPolicy) (acl.ACL, error) {
// Check if we can used the cached policy
if cached != nil && cached.ETag == p.ETag {
if p.TTL > 0 {
@ -159,7 +205,7 @@ func (s *Server) useACLPolicy(id, authDC string, cached *aclCacheEntry, p *struc
// Check for a cached compiled policy
var compiled acl.ACL
raw, ok := s.aclPolicyCache.Get(p.ETag)
raw, ok := c.policies.Get(p.ETag)
if ok {
compiled = raw.(acl.ACL)
} else {
@ -167,7 +213,7 @@ func (s *Server) useACLPolicy(id, authDC string, cached *aclCacheEntry, p *struc
parent := acl.RootACL(p.Parent)
if parent == nil {
var err error
parent, err = s.lookupACL(p.Parent, authDC)
parent, err = c.lookupACL(p.Parent, authDC)
if err != nil {
return nil, err
}
@ -180,7 +226,7 @@ func (s *Server) useACLPolicy(id, authDC string, cached *aclCacheEntry, p *struc
}
// Cache the policy
s.aclPolicyCache.Add(p.ETag, acl)
c.policies.Add(p.ETag, acl)
compiled = acl
}
@ -192,7 +238,7 @@ func (s *Server) useACLPolicy(id, authDC string, cached *aclCacheEntry, p *struc
if p.TTL > 0 {
cached.Expires = time.Now().Add(p.TTL)
}
s.aclCache.Add(id, cached)
c.acls.Add(id, cached)
return compiled, nil
}