Pulling a docker image from Amazon ECR private registry consists of several steps; configuration, authentication, and using the right commands. In this post, let’s take a look at how to do that in Go.
Prerequisites
Install AWS SDK for Go
go get github.com/aws/aws-sdk-go-v2
go get github.com/aws/aws-sdk-go-v2/config
go get github.com/aws/aws-sdk-go-v2/credentials
go get github.com/aws/aws-sdk-go-v2/service/ecr
Install Docker SDK for Go
go get github.com/docker/docker/client
Pulling Docker Image
AWS in Go
First of all, we have to create a configuration object for AWS client. Let’s wrap that in its own function. Follow here for obtaining AWS access keys. Also, please note that I use WithRegion
and WithCredentialsProvider
functions to customize the default AWS configuration. I do that just for the demonstration, so feel free to remove those lines if you want to stick with the shared AWS configuration in your machine.
- load the default aws config; config files are located under the ~/.aws folder
- customize region; remove this line if customization is not needed
- customize credentials; remove this line if customization is not needed
func newAWSConfig(ctx context.Context) (aws.Config, error) {
cfg, err := awsconfig.LoadDefaultConfig( // #1
ctx,
awsconfig.WithRegion("<region>"), // #2
awsconfig.WithCredentialsProvider( // #3
credentials.NewStaticCredentialsProvider(
"<access-key>",
"<secret-access-key>",
"")))
if err != nil {
return aws.Config{}, fmt.Errorf("failed to load aws config: %w", err)
}
return cfg, nil
}
AWS configuration is done, and now we are ready to interact with the AWS services. Since we are dealing with Amazon ECR, we will create an Amazon ECR client for obtaining the login password to pull docker images.
cfg, err := newAWSConfig(ctx)
if err != nil {
panic(err)
}
cli := ecr.NewFromConfig(cfg)
Now, let’s obtain the login password.
- get token
- validate token data
- dereference pointer value for readability
- decode token since it’s base64 encoded
- decoded value is in form of “username:password”, so split it by “:”
- return the second entry of the split for the password
func getLoginPassword(ctx context.Context, ecr *ecr.Client) (string, error) {
// #1
tkn, err := ecr.GetAuthorizationToken(ctx, nil)
if err != nil {
return "", fmt.Errorf("failed to retrieve ecr token: %w", err)
}
// #2
if len(tkn.AuthorizationData) == 0 {
return "", fmt.Errorf("ecr token is empty")
}
if len(tkn.AuthorizationData) > 1 {
return "", fmt.Errorf("multiple ecr tokens: length: %d", len(tkn.AuthorizationData))
}
if tkn.AuthorizationData[0].AuthorizationToken == nil {
return "", fmt.Errorf("ecr token is nil")
}
// #3
str := *tkn.AuthorizationData[0].AuthorizationToken
// #4
dec, err := base64.URLEncoding.DecodeString(str)
if err != nil {
return "", fmt.Errorf("failed to decode ecr token: %w", err)
}
// #5
spl := strings.Split(string(dec), ":")
if len(spl) != 2 {
return "", fmt.Errorf("unexpected ecr token format")
}
// #6
return spl[1], nil
}
Docker in Go
Let’s start with creating a docker client.
func newDockerClient() (*client.Client, error) {
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
return nil, fmt.Errorf("failed to create docker client: %w", err)
}
return cli, nil
}
With the docker client we have just created, now we are ready to pull docker images from Amazon ECR using the ECR password. But first, we need to create an authentication configuration for the docker.
auth := types.AuthConfig{
Username: "AWS",
Password: password,
}
Finally, we are ready to pull docker images from Amazon ECR. Our image variable will be similar to this: <account-id>.dkr.ecr.<region>.amazonaws.com/<name>:<tag>
- encode auth config to json
- encode auth data to base64 string
- pull the image
- send logs to standard output
func pullImage(ctx context.Context, cli *client.Client, img string, auth types.AuthConfig) error {
// #1
authData, err := json.Marshal(auth)
if err != nil {
return err
}
// #2
auths := base64.URLEncoding.EncodeToString(authData)
// #3
out, err := cli.ImagePull(
ctx,
img,
types.ImagePullOptions{
RegistryAuth: auths,
})
if err != nil {
return fmt.Errorf("failed to pull image: %w", err)
}
defer out.Close()
// #4
_, err = io.Copy(os.Stdout, out)
if err != nil {
return fmt.Errorf("failed to read image logs: %w", err)
}
return nil
}
Conclusion
We managed to pull docker images from Amazon ECR. Before finishing up, we can do one more thing; cleaning up. That’s relatively simple, all we have to do is just calling the remove method.
func removeImage(ctx context.Context, cli *client.Client, img string) error {
_, err := cli.ImageRemove(
ctx,
img,
types.ImageRemoveOptions{})
if err != nil {
return fmt.Errorf("failed to remove image: %w", err)
}
return nil
}
Source code can be found on gist.