Skip to content

Instantly share code, notes, and snippets.

@wolfeidau
Created April 20, 2024 02:23
Show Gist options
  • Save wolfeidau/0a10079b4fc7764529e92dc39292ec4e to your computer and use it in GitHub Desktop.
Save wolfeidau/0a10079b4fc7764529e92dc39292ec4e to your computer and use it in GitHub Desktop.
Small SSM Cache
package cache
import (
"context"
"fmt"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ssm"
"github.com/patrickmn/go-cache"
"github.com/rs/zerolog/log"
)
type SSM interface {
GetParameter(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error)
}
type CallCacheSSMClient struct {
ssm SSM
call *CallCache
}
func NewCallCacheSSMClient(ssmClient SSM, expiration time.Duration, cleanupInterval time.Duration) SSM {
return &CallCacheSSMClient{ssm: ssmClient, call: &CallCache{cache: cache.New(expiration, cleanupInterval)}}
}
func (ssmc *CallCacheSSMClient) GetParameter(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) {
key := fmt.Sprintf("ssm:GetParameter:%s", aws.ToString(params.Name))
if val, exists := ssmc.call.cache.Get(key); exists {
log.Ctx(ctx).Info().Str("key", key).Msg("cached value returned")
return val.(*ssm.GetParameterOutput), nil
}
log.Ctx(ctx).Info().Str("key", key).Msg("loading value")
out, err := ssmc.ssm.GetParameter(ctx, params, optFns...)
if err != nil {
return nil, err
}
ssmc.call.cache.Set(key, out, cache.DefaultExpiration)
return out, nil
}
type CallCache struct {
cache *cache.Cache
}
package cache
import (
"context"
"testing"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ssm"
"github.com/aws/aws-sdk-go-v2/service/ssm/types"
"github.com/stretchr/testify/require"
)
func TestCallCacheSSMClient_GetParameter(t *testing.T) {
assert := require.New(t)
ssmMock := &ssmClientMock{}
ssmMock.GetParameterFunc = func(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) {
return &ssm.GetParameterOutput{
Parameter: &types.Parameter{
Value: aws.String("some value"),
},
}, nil
}
ssmClient := NewCallCacheSSMClient(ssmMock, 1*time.Minute, 10*time.Minute)
// First call not cached
out, err := ssmClient.GetParameter(context.TODO(), &ssm.GetParameterInput{
Name: aws.String("some.parameter"),
})
assert.NoError(err)
assert.Equal("some value", aws.ToString(out.Parameter.Value))
// Second call cached
out, err = ssmClient.GetParameter(context.TODO(), &ssm.GetParameterInput{
Name: aws.String("some.parameter"),
})
assert.NoError(err)
assert.Equal("some value", aws.ToString(out.Parameter.Value))
// Assert ssmMock.GetParameter was only called once
assert.Equal(1, ssmMock.GetParameterCallCount)
}
type ssmClientMock struct {
GetParameterFunc func(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error)
GetParameterCallCount int
}
func (mock *ssmClientMock) GetParameter(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) {
mock.GetParameterCallCount++
return mock.GetParameterFunc(ctx, params, optFns...)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment