cache for credentials
parent
08468c20e2
commit
72faa5566a
|
@ -6,6 +6,7 @@ import (
|
|||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
// "github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
|
@ -32,6 +33,7 @@ func init() {
|
|||
type CredentialsHelper struct {
|
||||
enabled bool
|
||||
region string
|
||||
cache *Cache
|
||||
}
|
||||
|
||||
// New creates a new instance of aws credentials helper
|
||||
|
@ -48,6 +50,7 @@ func New() *CredentialsHelper {
|
|||
ch.enabled = true
|
||||
log.Infof("extension.credentialshelper.aws: enabled")
|
||||
ch.region = region
|
||||
ch.cache = NewCache(2 * time.Hour)
|
||||
}
|
||||
|
||||
// if os.Getenv("AWS_ACCESS_KEY_ID") != "" && os.Getenv("AWS_SECRET_ACCESS_KEY") != "" && os.Getenv("AWS_REGION") != "" {
|
||||
|
@ -70,6 +73,11 @@ func (h *CredentialsHelper) GetCredentials(image *types.TrackedImage) (*types.Cr
|
|||
return nil, credentialshelper.ErrUnsupportedRegistry
|
||||
}
|
||||
|
||||
cached, err := h.cache.Get(registry)
|
||||
if err == nil {
|
||||
return cached, nil
|
||||
}
|
||||
|
||||
svc := ecr.New(session.New(), &aws.Config{
|
||||
Region: aws.String(h.region),
|
||||
})
|
||||
|
@ -116,10 +124,14 @@ func (h *CredentialsHelper) GetCredentials(image *types.TrackedImage) (*types.Cr
|
|||
return nil, fmt.Errorf("failed to decode authentication token: %s, error: %s", *ad.AuthorizationToken, err)
|
||||
}
|
||||
|
||||
return &types.Credentials{
|
||||
creds := &types.Credentials{
|
||||
Username: username,
|
||||
Password: password,
|
||||
}, nil
|
||||
}
|
||||
|
||||
h.cache.Put(registry, creds)
|
||||
|
||||
return creds, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/keel-hq/keel/types"
|
||||
)
|
||||
|
||||
type item struct {
|
||||
credentials *types.Credentials
|
||||
created time.Time
|
||||
}
|
||||
|
||||
// Cache - internal cache for aws
|
||||
type Cache struct {
|
||||
creds map[string]*item
|
||||
tick time.Duration
|
||||
ttl time.Duration
|
||||
mu *sync.RWMutex
|
||||
}
|
||||
|
||||
// NewCache - new credentials cache
|
||||
func NewCache(ttl time.Duration) (c *Cache) {
|
||||
c = &Cache{
|
||||
creds: make(map[string]*item),
|
||||
mu: &sync.RWMutex{},
|
||||
ttl: ttl,
|
||||
tick: 30 * time.Second,
|
||||
}
|
||||
go c.expiryService()
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Cache) expiryService() {
|
||||
ticker := time.NewTicker(c.tick)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
c.expire()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) expire() {
|
||||
c.mu.Lock()
|
||||
t := time.Now()
|
||||
for k, v := range c.creds {
|
||||
if t.Sub(v.created) > c.ttl {
|
||||
delete(c.creds, k)
|
||||
}
|
||||
}
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
// Put - saves new creds
|
||||
func (c *Cache) Put(registry string, creds *types.Credentials) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.creds[registry] = &item{credentials: creds, created: time.Now()}
|
||||
}
|
||||
|
||||
// Get - retrieves creds
|
||||
func (c *Cache) Get(registry string) (*types.Credentials, error) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
item, ok := c.creds[registry]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("not found")
|
||||
}
|
||||
|
||||
cr := new(types.Credentials)
|
||||
*cr = *item.credentials
|
||||
|
||||
return cr, nil
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/keel-hq/keel/types"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPutCreds(t *testing.T) {
|
||||
c := NewCache(time.Second * 5)
|
||||
|
||||
creds := &types.Credentials{
|
||||
Username: "user-1",
|
||||
Password: "pass-1",
|
||||
}
|
||||
|
||||
c.Put("reg1", creds)
|
||||
|
||||
stored, err := c.Get("reg1")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if stored.Username != "user-1" {
|
||||
t.Errorf("username mismatch: %s", stored.Username)
|
||||
}
|
||||
if stored.Password != "pass-1" {
|
||||
t.Errorf("password mismatch: %s", stored.Password)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpiry(t *testing.T) {
|
||||
c := &Cache{
|
||||
creds: make(map[string]*item),
|
||||
mu: &sync.RWMutex{},
|
||||
ttl: time.Millisecond * 500,
|
||||
tick: time.Millisecond * 100,
|
||||
}
|
||||
|
||||
go c.expiryService()
|
||||
|
||||
creds := &types.Credentials{
|
||||
Username: "user-1",
|
||||
Password: "pass-1",
|
||||
}
|
||||
|
||||
c.Put("reg1", creds)
|
||||
|
||||
time.Sleep(1100 * time.Millisecond)
|
||||
|
||||
_, err := c.Get("reg1")
|
||||
if err == nil {
|
||||
t.Fatalf("expected to get an error about missing record")
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue