github.com/olli-ai/jx/v2@v2.0.400-0.20210921045218-14731b4dd448/pkg/cloud/amazon/ecr.go (about)

     1  package amazon
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  	"strings"
     7  
     8  	"github.com/olli-ai/jx/v2/pkg/cloud/amazon/session"
     9  
    10  	"github.com/olli-ai/jx/v2/pkg/kube"
    11  	"k8s.io/client-go/kubernetes"
    12  
    13  	"github.com/aws/aws-sdk-go/aws"
    14  	"github.com/aws/aws-sdk-go/aws/awserr"
    15  	"github.com/aws/aws-sdk-go/service/ecr"
    16  	"github.com/aws/aws-sdk-go/service/sts"
    17  	"github.com/jenkins-x/jx-logging/pkg/log"
    18  	"github.com/olli-ai/jx/v2/pkg/util"
    19  )
    20  
    21  // GetAccountIDAndRegion returns the current account ID and region
    22  func GetAccountIDAndRegion(profile string, region string) (string, string, error) {
    23  	sess, err := session.NewAwsSession(profile, region)
    24  	// We nee to get the region from the connected cluster instead of the one configured for the calling user
    25  	// as it might not be found and it would then use the default (us-west-2)
    26  	_, region, err = session.GetCurrentlyConnectedRegionAndClusterName()
    27  	if err != nil {
    28  		return "", "", err
    29  	}
    30  	svc := sts.New(sess)
    31  
    32  	input := &sts.GetCallerIdentityInput{}
    33  
    34  	result, err := svc.GetCallerIdentity(input)
    35  	if err != nil {
    36  		return "", region, err
    37  	}
    38  	if result.Account != nil {
    39  		return *result.Account, region, nil
    40  	}
    41  	return "", region, fmt.Errorf("Could not find the AWS Account ID!")
    42  }
    43  
    44  // GetContainerRegistryHost
    45  func GetContainerRegistryHost() (string, error) {
    46  	accountId, region, err := GetAccountIDAndRegion("", "")
    47  	if err != nil {
    48  		return "", err
    49  	}
    50  	return accountId + ".dkr.ecr." + region + ".amazonaws.com", nil
    51  }
    52  
    53  /*
    54  Deprecated!
    55  
    56  This function is kept for backwards compatibility. AWS region should not be resolved from ECR address, but
    57  read from ConfigMap (see RememberRegion function). To keep backwards compatibility with existing installations this
    58  function will be kept for a while and it will perform migration to config map. Eventually it will be removed from a
    59  codebase.
    60  */
    61  //nolint
    62  func GetRegionFromContainerRegistryHost(kubeClient kubernetes.Interface, namespace string, dockerRegistry string) string {
    63  	submatch := regexp.MustCompile(`\.ecr\.(.*)\.amazonaws\.com$`).FindStringSubmatch(dockerRegistry)
    64  	if len(submatch) > 1 {
    65  		region := submatch[1]
    66  		// Migrating jx installations created before AWS region config map
    67  		kube.RememberRegion(kubeClient, namespace, region)
    68  		return region
    69  	} else {
    70  		return ""
    71  	}
    72  }
    73  
    74  // LazyCreateRegistry lazily creates the ECR registry if it does not already exist
    75  func LazyCreateRegistry(kube kubernetes.Interface, namespace string, region string, dockerRegistry string, orgName string, appName string) error {
    76  	// strip any tag/version from the app name
    77  	idx := strings.Index(appName, ":")
    78  	if idx > 0 {
    79  		appName = appName[0:idx]
    80  	}
    81  	repoName := appName
    82  	if orgName != "" {
    83  		repoName = orgName + "/" + appName
    84  	}
    85  	repoName = strings.ToLower(repoName)
    86  	log.Logger().Infof("Let's ensure that we have an ECR repository for the Docker image %s", util.ColorInfo(repoName))
    87  	if region == "" {
    88  		region = GetRegionFromContainerRegistryHost(kube, namespace, dockerRegistry)
    89  	}
    90  	sess, err := session.NewAwsSession("", region)
    91  	if err != nil {
    92  		return err
    93  	}
    94  	svc := ecr.New(sess)
    95  	repoInput := &ecr.DescribeRepositoriesInput{
    96  		RepositoryNames: []*string{
    97  			aws.String(repoName),
    98  		},
    99  	}
   100  	result, err := svc.DescribeRepositories(repoInput)
   101  	if aerr, ok := err.(awserr.Error); !ok || aerr.Code() != ecr.ErrCodeRepositoryNotFoundException {
   102  		return err
   103  	}
   104  	for _, repo := range result.Repositories {
   105  		name := repo.String()
   106  		log.Logger().Infof("Found repository: %s", name)
   107  		if name == repoName {
   108  			return nil
   109  		}
   110  	}
   111  	createRepoInput := &ecr.CreateRepositoryInput{
   112  		RepositoryName: aws.String(repoName),
   113  	}
   114  	createResult, err := svc.CreateRepository(createRepoInput)
   115  	if err != nil {
   116  		return fmt.Errorf("Failed to create the ECR repository for %s due to: %s", repoName, err)
   117  	}
   118  	repo := createResult.Repository
   119  	if repo != nil {
   120  		u := repo.RepositoryUri
   121  		if u != nil {
   122  			if !strings.HasPrefix(*u, dockerRegistry) {
   123  				log.Logger().Warnf("Created ECR repository (%s) doesn't match registry configured for team (%s)",
   124  					util.ColorInfo(*u), util.ColorInfo(dockerRegistry))
   125  			} else {
   126  				log.Logger().Infof("Created ECR repository: %s", util.ColorInfo(*u))
   127  			}
   128  		}
   129  	}
   130  	return nil
   131  }