secretcache/cache.go (101 lines of code) (raw):

// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You // may not use this file except in compliance with the License. A copy of // the License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file 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 secretcache provides the Cache struct for in-memory caching of secrets stored in AWS Secrets Manager // Also exports a CacheHook, for pre-store and post-fetch processing of cached values package secretcache import ( "context" "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/secretsmanager" smithymiddleware "github.com/aws/smithy-go/middleware" ) // Cache client for AWS Secrets Manager secrets. type Cache struct { lru *lruCache CacheConfig Client SecretsManagerAPIClient } // New constructs a secret cache using functional options, uses defaults otherwise. // Initialises a SecretsManager Client from a new config.LoadDefaultConfig. // Initialises CacheConfig to default values. // Initialises lru cache with a default max size. func New(optFns ...func(*Cache)) (*Cache, error) { cache := &Cache{ //Initialise default configuration CacheConfig: CacheConfig{ MaxCacheSize: DefaultMaxCacheSize, VersionStage: DefaultVersionStage, CacheItemTTL: DefaultCacheItemTTL, }, } // Iterate over options allowing user to specify alternate // configurations. for _, optFn := range optFns { optFn(cache) } //Initialise lru cache cache.lru = newLRUCache(cache.MaxCacheSize) //Initialise the secrets manager client if cache.Client == nil { cfg, err := config.LoadDefaultConfig(context.Background(), config.WithAPIOptions([]func(*smithymiddleware.Stack) error{ middleware.AddUserAgentKey(userAgent()), })) if err != nil { return nil, err } cache.Client = secretsmanager.NewFromConfig(cfg) } return cache, nil } // getCachedSecret gets a cached secret for the given secret identifier. // Returns cached secret item. func (c *Cache) getCachedSecret(secretId string) *secretCacheItem { lruValue, found := c.lru.get(secretId) if !found { cacheItem := newSecretCacheItem(c.CacheConfig, c.Client, secretId) c.lru.putIfAbsent(secretId, &cacheItem) lruValue, _ = c.lru.get(secretId) } secretCacheItem, _ := lruValue.(*secretCacheItem) return secretCacheItem } // GetSecretString gets the secret string value from the cache for given secret id and a default version stage. // Returns the secret string and an error if operation failed. func (c *Cache) GetSecretString(secretId string) (string, error) { return c.GetSecretStringWithContext(context.Background(), secretId) } func (c *Cache) GetSecretStringWithContext(ctx context.Context, secretId string) (string, error) { return c.GetSecretStringWithStageWithContext(ctx, secretId, DefaultVersionStage) } // GetSecretStringWithStage gets the secret string value from the cache for given secret id and version stage. // Returns the secret string and an error if operation failed. func (c *Cache) GetSecretStringWithStage(secretId string, versionStage string) (string, error) { return c.GetSecretStringWithStageWithContext(context.Background(), secretId, versionStage) } func (c *Cache) GetSecretStringWithStageWithContext(ctx context.Context, secretId string, versionStage string) (string, error) { secretCacheItem := c.getCachedSecret(secretId) getSecretValueOutput, err := secretCacheItem.getSecretValue(ctx, versionStage) if err != nil { return "", err } if getSecretValueOutput.SecretString == nil { return "", &InvalidOperationError{ baseError{ Message: "requested secret version does not contain SecretString", }, } } return *getSecretValueOutput.SecretString, nil } // GetSecretBinary gets the secret binary value from the cache for given secret id and a default version stage. // Returns the secret binary and an error if operation failed. func (c *Cache) GetSecretBinary(secretId string) ([]byte, error) { return c.GetSecretBinaryWithContext(context.Background(), secretId) } func (c *Cache) GetSecretBinaryWithContext(ctx context.Context, secretId string) ([]byte, error) { return c.GetSecretBinaryWithStageWithContext(ctx, secretId, DefaultVersionStage) } // GetSecretBinaryWithStage gets the secret binary value from the cache for given secret id and version stage. // Returns the secret binary and an error if operation failed. func (c *Cache) GetSecretBinaryWithStage(secretId string, versionStage string) ([]byte, error) { return c.GetSecretBinaryWithStageWithContext(context.Background(), secretId, versionStage) } func (c *Cache) GetSecretBinaryWithStageWithContext(ctx context.Context, secretId string, versionStage string) ([]byte, error) { secretCacheItem := c.getCachedSecret(secretId) getSecretValueOutput, err := secretCacheItem.getSecretValue(ctx, versionStage) if err != nil { return nil, err } if getSecretValueOutput.SecretBinary == nil { return nil, &InvalidOperationError{ baseError{ Message: "requested secret version does not contain SecretBinary", }, } } return getSecretValueOutput.SecretBinary, nil } // Method to force the refresh of a secret inside the cache func (c *Cache) RefreshNow(secretId string) { c.RefreshNowWithContext(context.Background(), secretId) } func (c *Cache) RefreshNowWithContext(ctx context.Context, secretId string) { secretCacheItem := c.getCachedSecret(secretId) secretCacheItem.refreshNow(ctx) }