// Copyright 2019 Drone IO, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package orgs import ( "context" "fmt" "sync" "time" "github.com/drone/drone/core" lru "github.com/hashicorp/golang-lru" ) // content key pattern used in the cache, comprised of the // organization name and username. const contentKey = "%s/%s" // NewCache wraps the service with a simple cache to store // organization membership. func NewCache(base core.OrganizationService, size int, ttl time.Duration) core.OrganizationService { // simple cache prevents the same yaml file from being // requested multiple times in a short period. cache, _ := lru.New(25) return &cacher{ cache: cache, base: base, size: size, ttl: ttl, } } type cacher struct { mu sync.Mutex base core.OrganizationService size int ttl time.Duration cache *lru.Cache } type item struct { expiry time.Time member bool admin bool } func (c *cacher) List(ctx context.Context, user *core.User) ([]*core.Organization, error) { return c.base.List(ctx, user) } func (c *cacher) Membership(ctx context.Context, user *core.User, name string) (bool, bool, error) { key := fmt.Sprintf(contentKey, user.Login, name) now := time.Now() // get the membership details from the cache. cached, ok := c.cache.Get(key) if ok { item := cached.(*item) // if the item is expired it can be ejected // from the cache, else if not expired we return // the cached results. if now.After(item.expiry) { c.cache.Remove(cached) } else { return item.member, item.admin, nil } } // get up-to-date membership details due to a cache // miss or expired cache item. member, admin, err := c.base.Membership(ctx, user, name) if err != nil { return false, false, err } c.cache.Add(key, &item{ expiry: now.Add(c.ttl), member: member, admin: admin, }) return member, admin, nil }