How to Setup Golang AWS SDK V2 with LocalStack for Local Development

phuynh39
2 min readFeb 10, 2021
Cloud in Local

AWS just announces General Availability (GA) release of AWS SDK version 2 for Go in last month. There are significant performance improvements and interfaces changing in the version 2 which you can find in details here: https://aws.amazon.com/blogs/developer/aws-sdk-for-go-version-2-general-availability

Since AWS changes the way of how to do configuration on version 2, it requires a new setup for AWS to running locally with Go and LocalStack. In this post, I will show how we can setup local development environment using new version of AWS SDK.

The key of configuration for AWS service to run locally is at line 35, where we have to create a custom endpoint resolver. The SDK will use this to resolve the endpoint where AWS services are hosted. In this case, we will point them to AWS_ENDPOINT environment variable, and its value is set to LocalStack host address (http://awsservices:4566). When AWS_ENDPOINT is empty, the SDK cannot resolve the LocalStack host, then it will fall back to the default resolver which points to the real AWS services.

package main


import (
"bytes"
"context"
"fmt"
"log"
"os"

"github.com/aws/aws-lambda-go/lambda"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
)

// CustomEvent for lambda
type CustomEvent struct {
ID string
Name string
}

var (
awsRegion string
awsEndpoint string
bucketName string

s3svc *s3.Client
)

func init() {
awsRegion = os.Getenv("AWS_REGION")
awsEndpoint = os.Getenv("AWS_ENDPOINT")
bucketName = os.Getenv("S3_BUCKET")

customResolver := aws.EndpointResolverFunc(func(service, region string) (aws.Endpoint, error) {
if awsEndpoint != "" {
return aws.Endpoint{
PartitionID: "aws",
URL: awsEndpoint,
SigningRegion: awsRegion,
}, nil
}

// returning EndpointNotFoundError will allow the service to fallback to it's default resolution
return aws.Endpoint{}, &aws.EndpointNotFoundError{}
})

awsCfg, err := config.LoadDefaultConfig(context.TODO(),
config.WithRegion(awsRegion),
config.WithEndpointResolver(customResolver),
)
if err != nil {
log.Fatalf("Cannot load the AWS configs: %s", err)
}

s3svc = s3.NewFromConfig(awsCfg, func(o *s3.Options) {
o.UsePathStyle = true
})
}

func handler(ctx context.Context, event CustomEvent) error {
s3Key := fmt.Sprintf("%s.txt", event.ID)
body := []byte(fmt.Sprintf("Hello, %s", event.Name))
resp, err := s3svc.PutObject(context.TODO(), &s3.PutObjectInput{
Bucket: aws.String(bucketName),
Key: aws.String(s3Key),
Body: bytes.NewReader(body),
ContentLength: int64(len(body)),
ContentType: aws.String("application/text"),
ContentDisposition: aws.String("attachment"),
})
log.Printf("S3 PutObject response: %+v", resp)
if err != nil {
return err
}

return nil
}

func main() {
lambda.Start(handler)
}

For the full project setup, you can going through the instruction in this repo: https://github.com/KOBA-Systems/aws_local_dev_setup

--

--

phuynh39

Experienced Full-Stack Software Engineer with a demonstrated 8-year history of working in the computer software industry