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:
parent
4214464065
commit
b2d9782a9e
|
@ -40,6 +40,8 @@ type Config struct {
|
||||||
LoginPassword string `mapstructure:"login_password"`
|
LoginPassword string `mapstructure:"login_password"`
|
||||||
LoginServer string `mapstructure:"login_server"`
|
LoginServer string `mapstructure:"login_server"`
|
||||||
LoginUsername string `mapstructure:"login_username"`
|
LoginUsername string `mapstructure:"login_username"`
|
||||||
|
EcrLogin bool `mapstructure:"ecr_login"`
|
||||||
|
AwsAccessConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
ctx interpolate.Context
|
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 {
|
if errs != nil && len(errs.Errors) > 0 {
|
||||||
return nil, nil, errs
|
return nil, nil, errs
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -21,7 +21,22 @@ func (s *StepPull) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
|
||||||
ui.Say(fmt.Sprintf("Pulling Docker image: %s", config.Image))
|
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...")
|
ui.Message("Logging in...")
|
||||||
err := driver.Login(
|
err := driver.Login(
|
||||||
config.LoginServer,
|
config.LoginServer,
|
||||||
|
|
|
@ -20,6 +20,8 @@ type Config struct {
|
||||||
LoginUsername string `mapstructure:"login_username"`
|
LoginUsername string `mapstructure:"login_username"`
|
||||||
LoginPassword string `mapstructure:"login_password"`
|
LoginPassword string `mapstructure:"login_password"`
|
||||||
LoginServer string `mapstructure:"login_server"`
|
LoginServer string `mapstructure:"login_server"`
|
||||||
|
EcrLogin bool `mapstructure:"ecr_login"`
|
||||||
|
docker.AwsAccessConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
ctx interpolate.Context
|
ctx interpolate.Context
|
||||||
}
|
}
|
||||||
|
@ -42,6 +44,9 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if p.config.EcrLogin && p.config.LoginServer == "" {
|
||||||
|
return fmt.Errorf("ECR login requires login server to be provided.")
|
||||||
|
}
|
||||||
return nil
|
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}
|
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...")
|
ui.Message("Logging in...")
|
||||||
err := driver.Login(
|
err := driver.Login(
|
||||||
p.config.LoginServer,
|
p.config.LoginServer,
|
||||||
|
|
|
@ -86,9 +86,27 @@ You must specify (only) one of `commit`, `discard`, or `export_path`.
|
||||||
|
|
||||||
### Optional:
|
### 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
|
- `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
|
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.
|
- `login_email` (string) - The email to use to authenticate to login.
|
||||||
|
|
||||||
|
@ -237,10 +255,9 @@ shown below:
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "docker-push",
|
"type": "docker-push",
|
||||||
"login": true,
|
"ecr_login": true,
|
||||||
"login_email": "none",
|
"aws_access_key": "YOUR KEY HERE",
|
||||||
"login_username": "AWS",
|
"aws_secret_key": "YOUR SECRET KEY HERE",
|
||||||
"login_password": "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
||||||
"login_server": "https://12345.dkr.ecr.us-east-1.amazonaws.com/"
|
"login_server": "https://12345.dkr.ecr.us-east-1.amazonaws.com/"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -248,10 +265,7 @@ shown below:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
See the
|
[Learn how to set Amazon AWS credentials.](/docs/builders/amazon.html#specifying-amazon-credentials)
|
||||||
[AWS documentation](https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html)
|
|
||||||
for steps to obtain Amazon ECR registry credentials.
|
|
||||||
|
|
||||||
|
|
||||||
## Dockerfiles
|
## 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
|
etc. to provision your Docker container just like you would a regular
|
||||||
virtualized or dedicated machine.
|
virtualized or dedicated machine.
|
||||||
|
|
||||||
While Docker has many features, Packer views Docker simply as an LXC container
|
While Docker has many features, Packer views Docker simply as an container
|
||||||
runner. To that end, Packer is able to repeatably build these LXC containers
|
runner. To that end, Packer is able to repeatably build these containers
|
||||||
using portable provisioning scripts.
|
using portable provisioning scripts.
|
||||||
|
|
||||||
Dockerfiles have some additional features that Packer doesn't support which are
|
Dockerfiles have some additional features that Packer doesn't support which are
|
||||||
|
|
|
@ -18,8 +18,26 @@ pushes it to a Docker registry.
|
||||||
|
|
||||||
This post-processor has only optional configuration:
|
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` (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.
|
- `login_email` (string) - The email to use to authenticate to login.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue