Improved support for Amazon EC2 Container Registry - ECR

This adds support for authenticating towards ECR in the docker builder
and docker-push post-processor using them same mechanisms as in the
amazon builders. I.g. access key/secret key, credentials on file,
environment variables, sts tokens or IAM instance roles.
This commit is contained in:
Rickard von Essen 2016-09-03 16:45:52 +02:00
parent 4214464065
commit b2d9782a9e
No known key found for this signature in database
GPG Key ID: E0C0327388876CBA
6 changed files with 185 additions and 27 deletions

View File

@ -35,11 +35,13 @@ type Config struct {
// This is used to login to dockerhub to pull a private base container. For
// pushing to dockerhub, see the docker post-processors
Login bool
LoginEmail string `mapstructure:"login_email"`
LoginPassword string `mapstructure:"login_password"`
LoginServer string `mapstructure:"login_server"`
LoginUsername string `mapstructure:"login_username"`
Login bool
LoginEmail string `mapstructure:"login_email"`
LoginPassword string `mapstructure:"login_password"`
LoginServer string `mapstructure:"login_server"`
LoginUsername string `mapstructure:"login_username"`
EcrLogin bool `mapstructure:"ecr_login"`
AwsAccessConfig `mapstructure:",squash"`
ctx interpolate.Context
}
@ -107,6 +109,10 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
}
}
if c.EcrLogin && c.LoginServer == "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("ECR login requires login server to be provided."))
}
if errs != nil && len(errs.Errors) > 0 {
return nil, nil, errs
}

View File

@ -0,0 +1,88 @@
package docker
import (
"encoding/base64"
"fmt"
"log"
"regexp"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
"github.com/aws/aws-sdk-go/aws/ec2metadata"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ecr"
)
type AwsAccessConfig struct {
AccessKey string `mapstructure:"aws_access_key"`
SecretKey string `mapstructure:"aws_secret_key"`
Token string `mapstructure:"aws_token"`
}
// Config returns a valid aws.Config object for access to AWS services, or
// an error if the authentication and region couldn't be resolved
func (c *AwsAccessConfig) config(region string) (*aws.Config, error) {
var creds *credentials.Credentials
config := aws.NewConfig().WithRegion(region).WithMaxRetries(11)
sess := session.New(config)
creds = credentials.NewChainCredentials([]credentials.Provider{
&credentials.StaticProvider{Value: credentials.Value{
AccessKeyID: c.AccessKey,
SecretAccessKey: c.SecretKey,
SessionToken: c.Token,
}},
&credentials.EnvProvider{},
&credentials.SharedCredentialsProvider{Filename: "", Profile: ""},
&ec2rolecreds.EC2RoleProvider{
Client: ec2metadata.New(sess),
},
})
return config.WithCredentials(creds), nil
}
// Get a login token for Amazon AWS ECR. Returns username and password
// or an error.
func (c *AwsAccessConfig) EcrGetLogin(ecrUrl string) (string, string, error) {
exp := regexp.MustCompile("(?:http://|https://|)([0-9]*)\\.dkr\\.ecr\\.(.*)\\.amazonaws\\.com.*")
splitUrl := exp.FindStringSubmatch(ecrUrl)
accountId := splitUrl[1]
region := splitUrl[2]
log.Println(fmt.Sprintf("Getting ECR token for account: %s in %s..", accountId, region))
awsConfig, err := c.config(region)
if err != nil {
return "", "", err
}
session, err := session.NewSession(awsConfig)
if err != nil {
return "", "", fmt.Errorf("failed to create session: %s", err)
}
service := ecr.New(session)
params := &ecr.GetAuthorizationTokenInput{
RegistryIds: []*string{
aws.String(accountId),
},
}
resp, err := service.GetAuthorizationToken(params)
if err != nil {
return "", "", fmt.Errorf(err.Error())
}
auth, err := base64.StdEncoding.DecodeString(*resp.AuthorizationData[0].AuthorizationToken)
if err != nil {
return "", "", fmt.Errorf("Error decoding ECR AuthorizationToken: %s", err)
}
authParts := strings.SplitN(string(auth), ":", 2)
log.Printf("Successfully got login for ECR: %s", ecrUrl)
return authParts[0], authParts[1], nil
}

View File

@ -21,7 +21,22 @@ func (s *StepPull) Run(state multistep.StateBag) multistep.StepAction {
ui.Say(fmt.Sprintf("Pulling Docker image: %s", config.Image))
if config.Login {
if config.EcrLogin {
ui.Message("Fetching ECR credentials...")
username, password, err := config.EcrGetLogin(config.LoginServer)
if err != nil {
err := fmt.Errorf("Error fetching ECR credentials: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
config.LoginUsername = username
config.LoginPassword = password
}
if config.Login || config.EcrLogin {
ui.Message("Logging in...")
err := driver.Login(
config.LoginServer,

View File

@ -15,11 +15,13 @@ import (
type Config struct {
common.PackerConfig `mapstructure:",squash"`
Login bool
LoginEmail string `mapstructure:"login_email"`
LoginUsername string `mapstructure:"login_username"`
LoginPassword string `mapstructure:"login_password"`
LoginServer string `mapstructure:"login_server"`
Login bool
LoginEmail string `mapstructure:"login_email"`
LoginUsername string `mapstructure:"login_username"`
LoginPassword string `mapstructure:"login_password"`
LoginServer string `mapstructure:"login_server"`
EcrLogin bool `mapstructure:"ecr_login"`
docker.AwsAccessConfig `mapstructure:",squash"`
ctx interpolate.Context
}
@ -42,6 +44,9 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
return err
}
if p.config.EcrLogin && p.config.LoginServer == "" {
return fmt.Errorf("ECR login requires login server to be provided.")
}
return nil
}
@ -60,7 +65,19 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac
driver = &docker.DockerDriver{Ctx: &p.config.ctx, Ui: ui}
}
if p.config.Login {
if p.config.EcrLogin {
ui.Message("Fetching ECR credentials...")
username, password, err := p.config.EcrGetLogin(p.config.LoginServer)
if err != nil {
return nil, false, err
}
p.config.LoginUsername = username
p.config.LoginPassword = password
}
if p.config.Login || p.config.EcrLogin {
ui.Message("Logging in...")
err := driver.Login(
p.config.LoginServer,

View File

@ -86,9 +86,27 @@ You must specify (only) one of `commit`, `discard`, or `export_path`.
### Optional:
- `aws_access_key` (string) - The AWS access key used to communicate with AWS.
[Learn how to set this.](/docs/builders/amazon.html#specifying-amazon-credentials)
- `aws_secret_key` (string) - The AWS secret key used to communicate with AWS.
[Learn how to set this.](/docs/builders/amazon.html#specifying-amazon-credentials)
- `aws_token` (string) - The AWS access token to use. This is different from the
access key and secret key. If you're not sure what this is, then you
probably don't need it. This will also be read from the `AWS_SESSION_TOKEN`
environmental variable.
- `ecr_login` (boolean) - Defaults to false. If true, the builder will login in
order to pull the image from
[Amazon EC2 Container Registry (ECR)](https://aws.amazon.com/ecr/).
The builder only logs in for the duration of the pull. If true
`login_server` is required and `login`, `login_username`, and
`login_password` will be ignored.
- `login` (boolean) - Defaults to false. If true, the builder will login in
order to pull the image. The builder only logs in for the duration of
the pull. It always logs out afterwards.
the pull. It always logs out afterwards. For log into ECR see `ecr_login`.
- `login_email` (string) - The email to use to authenticate to login.
@ -237,10 +255,9 @@ shown below:
},
{
"type": "docker-push",
"login": true,
"login_email": "none",
"login_username": "AWS",
"login_password": "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"ecr_login": true,
"aws_access_key": "YOUR KEY HERE",
"aws_secret_key": "YOUR SECRET KEY HERE",
"login_server": "https://12345.dkr.ecr.us-east-1.amazonaws.com/"
}
]
@ -248,10 +265,7 @@ shown below:
}
```
See the
[AWS documentation](https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html)
for steps to obtain Amazon ECR registry credentials.
[Learn how to set Amazon AWS credentials.](/docs/builders/amazon.html#specifying-amazon-credentials)
## Dockerfiles
@ -263,8 +277,8 @@ Instead, you can just provide shell scripts, Chef recipes, Puppet manifests,
etc. to provision your Docker container just like you would a regular
virtualized or dedicated machine.
While Docker has many features, Packer views Docker simply as an LXC container
runner. To that end, Packer is able to repeatably build these LXC containers
While Docker has many features, Packer views Docker simply as an container
runner. To that end, Packer is able to repeatably build these containers
using portable provisioning scripts.
Dockerfiles have some additional features that Packer doesn't support which are

View File

@ -18,8 +18,26 @@ pushes it to a Docker registry.
This post-processor has only optional configuration:
- `aws_access_key` (string) - The AWS access key used to communicate with AWS.
[Learn how to set this.](/docs/builders/amazon.html#specifying-amazon-credentials)
- `aws_secret_key` (string) - The AWS secret key used to communicate with AWS.
[Learn how to set this.](/docs/builders/amazon.html#specifying-amazon-credentials)
- `aws_token` (string) - The AWS access token to use. This is different from the
access key and secret key. If you're not sure what this is, then you
probably don't need it. This will also be read from the `AWS_SESSION_TOKEN`
environmental variable.
- `ecr_login` (boolean) - Defaults to false. If true, the post-processor
will login in order to push the image to
[Amazon EC2 Container Registry (ECR)](https://aws.amazon.com/ecr/).
The post-processor only logs in for the duration of the push. If true
`login_server` is required and `login`, `login_username`, and
`login_password` will be ignored.
- `login` (boolean) - Defaults to false. If true, the post-processor will
login prior to pushing.
login prior to pushing. For log into ECR see `ecr_login`.
- `login_email` (string) - The email to use to authenticate to login.
@ -29,9 +47,9 @@ This post-processor has only optional configuration:
- `login_server` (string) - The server address to login to.
Note: When using _Docker Hub_ or _Quay_ registry servers, `login` must to be
set to `true` and `login_email`, `login_username`, **and** `login_password`
must to be set to your registry credentials. When using Docker Hub,
Note: When using _Docker Hub_ or _Quay_ registry servers, `login` must to be
set to `true` and `login_email`, `login_username`, **and** `login_password`
must to be set to your registry credentials. When using Docker Hub,
`login_server` can be omitted.
-> **Note:** If you login using the credentials above, the post-processor