Testing DynamoDB locally in Go or any language is tricky. In this quick article I will provide a solution on how to test locally using a docker container and your web service that interfaces with AWS DynamoDB.
First, you will need a DynamoDB instance running locally so that you can interact with it from your service.
DynamoDB Instance with Docker-Compose
dynamodb:
image: "amazon/dynamodb-local:latest"
command: "-jar DynamoDBLocal.jar -sharedDb -dbPath ./data"
container_name: dynamodb
ports:
- 8000:8000
environment:
AWS_REGION: "us-east-1"
AWS_ACCESS_KEY_ID: "YOUR_AKID"
AWS_SECRET_ACCESS_KEY: "YOUR_SECRET_KEY"
AWS_SESSION_TOKEN: "TOKEN"
working_dir: "/home/dynamodblocal"
volumes:
- "./docker/dynamodb:/home/dynamodblocal/data"
The code above shows you how to create a simple DynamoDB instance for you to test with locally user a docker-compose.yml file. In this article we are assuming that you are running your webservice that has visibility to the DynamoDB container, the setup of the web service is out of scope for this article.
When using AWS SDK v2 for Golang things are done way differently then on version v1. In v1, you could provide an endpoint, a region and credentials that required for you to test locally. Just for reference, this is how you test in aws-sdk-go-v1:
creds := credentials.NewEnvCredentials()
_, err := creds.Get()
if err != nil {
return nil, err
}
sess, err := session.NewSession(&aws.Config{
Region: aws.String("us-east-1"),
Endpoint: aws.String("http://localhost:8000"),
Credentials: creds},
)
if err != nil {
return nil, err
}
// Create DynamoDB client
svc := dynamodb.New(sess)
The endpoint value is whatever url you specified in your docker-compose file for you AWS DynamoDB instance.
Implement AWS SDK Go v2
Now to answer the question of this topic: Testing locally DynamoDB, how can we accomplish this? Compared to v1, we have to do things differently. In v2 you have to have a several awsconfig.LoadOptionFunc’s. You will need the WithRegion
, WithEndpointResolver
, and lastly for local testing WithCredentialsProvider
. For v2, make sure you have an IAM Role that is able to talk to your ec2 instance or whatever infrastructure you are using to serve your app and the DynamoDB instance.
customResolver := aws.EndpointResolverFunc(func(service, region string) (aws.Endpoint, error) {
if os.Getenv("APP_ENV") == "local" {
return aws.Endpoint{
URL: config.Dynamo.Endpoint,
}, nil
}
// returning EndpointNotFoundError will allow the service to fallback to it's default resolution
return aws.Endpoint{}, &aws.EndpointNotFoundError{}
})
var (
cfg aws.Config
err error
)
if os.Getenv("APP_ENV") == "local" {
cfg, err = awsconfig.LoadDefaultConfig(context.TODO(), awsconfig.WithRegion("us-east-1"), awsconfig.WithEndpointResolver(customResolver), awsconfig.WithCredentialsProvider(credentials.StaticCredentialsProvider{
Value: aws.Credentials{
AccessKeyID: "dummy", SecretAccessKey: "dummy", SessionToken: "dummy",
Source: "Hard-coded credentials; values are irrelevant for local DynamoDB",
},
}))
} else {
cfg, err = awsconfig.LoadDefaultConfig(context.TODO(), awsconfig.WithRegion("us-east-1"), awsconfig.WithEndpointResolver(customResolver))
}
if err != nil {
panic(err)
}
svc := dynamodb.NewFromConfig(cfg)
In the code above, you will notice that I check for an environment variable to equal “local”. The reason for this is, in v2 you are discouraged from using AWS access keys. When you test locally, the container requires an AWS access key, therefore you can create dummy credentials to use when testing locally.
Thank you for taking the time to read this information. You can find other articles here.