Skip to content

Commit

Permalink
Implemented resource tagging for ECR repositories
Browse files Browse the repository at this point in the history
  • Loading branch information
PettitWesley committed Feb 22, 2019
1 parent 6e67622 commit 5407919
Show file tree
Hide file tree
Showing 17 changed files with 2,320 additions and 71 deletions.
4 changes: 3 additions & 1 deletion ecs-cli/Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

117 changes: 96 additions & 21 deletions ecs-cli/modules/cli/image/image_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,15 @@ import (

ecrclient "github.com/aws/amazon-ecs-cli/ecs-cli/modules/clients/aws/ecr"
stsclient "github.com/aws/amazon-ecs-cli/ecs-cli/modules/clients/aws/sts"
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/clients/aws/tagging"
dockerclient "github.com/aws/amazon-ecs-cli/ecs-cli/modules/clients/docker"
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/commands/flags"
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/config"
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/utils"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/arn"
"github.com/aws/aws-sdk-go/service/ecr"
taggingSDK "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi"
units "github.com/docker/go-units"
docker "github.com/fsouza/go-dockerclient"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -68,8 +72,11 @@ func ImagePush(c *cli.Context) {

ecrClient := getECRClient(c, commandConfig)
stsClient := stsclient.NewClient(commandConfig)
taggingClient := tagging.NewTaggingClient(commandConfig)

if err := pushImage(c, rdwr, dockerClient, ecrClient, stsClient); err != nil {
region := aws.StringValue(commandConfig.Session.Config.Region)

if err := pushImage(c, region, dockerClient, ecrClient, stsClient, taggingClient); err != nil {
logrus.Fatal("Error executing 'push': ", err)
}
}
Expand Down Expand Up @@ -134,7 +141,7 @@ func getECRClient(c *cli.Context, commandConfig *config.CommandConfig) ecrclient
return ecrClient
}

func pushImage(c *cli.Context, rdwr config.ReadWriter, dockerClient dockerclient.Client, ecrClient ecrclient.Client, stsClient stsclient.Client) error {
func pushImage(c *cli.Context, region string, dockerClient dockerclient.Client, ecrClient ecrclient.Client, stsClient stsclient.Client, taggingClient tagging.Client) error {
registryID := c.String(flags.RegistryIdFlag)

args := c.Args()
Expand All @@ -150,6 +157,20 @@ func pushImage(c *cli.Context, rdwr config.ReadWriter, dockerClient dockerclient
return err
}

// For tagging (need the full ARN) and ECR auth, we need the registry ID
// We can get this either from the registry URI or from STS
if registryURI == "" {
registryID, err = getRegistryID(registryID, stsClient)
if err != nil {
return err
}
} else {
registryID, err = getRegistryIDFromURI(registryURI)
if err != nil {
return err
}
}

ecrAuth, err := getECRAuth(registryURI, registryID, stsClient, ecrClient)
if err != nil {
return err
Expand All @@ -171,6 +192,20 @@ func pushImage(c *cli.Context, rdwr config.ReadWriter, dockerClient dockerclient
}
}

if tagVal := c.String(flags.ResourceTagsFlag); tagVal != "" {
tags, err := utils.GetTagsMap(tagVal)
if err != nil {
return err
}
logrus.WithField("repository", repository).Info("Tagging repository...")
repoARN := getRepoARN(region, registryID, repository)
logrus.Debugf("Using repository ARN: %s", repoARN)
err = tagRepo(repoARN, tags, taggingClient)
if err != nil {
return err
}
}

// Push Image to ECR
dockerAuth := docker.AuthConfiguration{
Username: ecrAuth.Username,
Expand All @@ -195,6 +230,11 @@ func pullImage(c *cli.Context, rdwr config.ReadWriter, dockerClient dockerclient
return err
}

registryID, err = getRegistryID(registryID, stsClient)
if err != nil {
return err
}

ecrAuth, err := getECRAuth(registryURI, registryID, stsClient, ecrClient)
if err != nil {
return err
Expand Down Expand Up @@ -272,12 +312,12 @@ func listImagesContent(w *tabwriter.Writer, info imageInfo, count int) {

func printImageRow(w io.Writer, info imageInfo) {
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t\n",
info.RepositoryName,
info.Tag,
info.ImageDigest,
info.PushedAt,
info.Size,
)
info.RepositoryName,
info.Tag,
info.ImageDigest,
info.PushedAt,
info.Size,
)
}

func getTagStatus(c *cli.Context) string {
Expand Down Expand Up @@ -306,25 +346,60 @@ func getECRAuth(registryURI string, registryID string,
stsClient stsclient.Client, ecrClient ecrclient.Client) (*ecrclient.Auth, error) {

if registryURI == "" {
id, err := getRegistryID(registryID, stsClient)
if err != nil {
return nil, err
}
return ecrClient.GetAuthorizationTokenByID(id)
return ecrClient.GetAuthorizationTokenByID(registryID)
}

return ecrClient.GetAuthorizationToken(registryURI)
}

func splitImageName(image string, seperatorRegExp string, format string) (registry string, repository string, tag string, err error) {
func splitImageName(image string, seperatorRegExp string, format string) (registry string, repository string, tag string, err error) {
re := regexp.MustCompile(
`^(?:((?:[a-zA-Z0-9][a-zA-Z0-9-_]*)\.dkr\.ecr(\-fips)?\.[a-zA-Z0-9\-_]+\.amazonaws\.com(?:\.cn)?)/)?` + // registry uri (Optional)
`([0-9a-z\-_/]+)` + // repository
`(?:` + seperatorRegExp + `([0-9A-Za-z_.\-:]+))?$`) // tag or sha (Optional)
matches := re.FindStringSubmatch(image)
if len(matches) == 0 {
return "", "", "", fmt.Errorf("Please specify the image name in the correct format [%s]", format)
}
`([0-9a-z\-_/]+)` + // repository
`(?:` + seperatorRegExp + `([0-9A-Za-z_.\-:]+))?$`) // tag or sha (Optional)
matches := re.FindStringSubmatch(image)
if len(matches) == 0 {
return "", "", "", fmt.Errorf("Please specify the image name in the correct format [%s]", format)
}

return matches[1], matches[3], matches[4], nil
}

func getRegistryIDFromURI(registryURI string) (string, error) {
re := regexp.MustCompile(`^([a-zA-Z0-9][a-zA-Z0-9-_]*)\.dkr\.ecr(\-fips)?\.[a-zA-Z0-9\-_]+\.amazonaws(\.com)?(\.cn)?`)
matches := re.FindStringSubmatch(registryURI)
if len(matches) < 2 {
return "", fmt.Errorf("Could not parse account ID from registry URI; URI=%s", registryURI)
}
return matches[1], nil
}

func tagRepo(repoARN string, tags map[string]*string, taggingClient tagging.Client) error {
input := &taggingSDK.TagResourcesInput{
ResourceARNList: aws.StringSlice([]string{
repoARN,
}),
Tags: tags,
}
output, err := taggingClient.TagResources(input)
if err != nil {
return err
}

return matches[1], matches[3], matches[4], nil
for resource, info := range output.FailedResourcesMap {
return fmt.Errorf("Failed to tag respository %s; error=%s", resource, *info.ErrorMessage)
}
return nil
}

// arn:aws:ecr:region:account-id:repository/repository-name
func getRepoARN(region, accountID, repository string) string {
repoARN := arn.ARN{
Partition: utils.GetPartition(region),
Service: "ecr",
Region: region,
AccountID: accountID,
Resource: "repository/" + repository,
}
return repoARN.String()
}
Loading

0 comments on commit 5407919

Please sign in to comment.