Source code for ecs_composex.compose.compose_services.service_image.ecr_helpers

#  SPDX-License-Identifier: MPL-2.0
#  Copyright 2020-2022 John Mille <john@compose-x.io>

import re

from boto3.session import Session
from compose_x_common.aws.ecr.images import list_all_images
from compose_x_common.compose_x_common import keyisset, set_else_none

from ecs_composex.common.aws import get_cross_role_session
from ecs_composex.common.logging import LOG

ECR_URI_RE = re.compile(
    r"(?P<account_id>\d{12}).dkr.ecr.(?P<region>[a-z0-9-]+).amazonaws.com/"
    r"(?P<repo_name>[a-zA-Z0-9-_./]+)(?P<tag>(?:\@sha[\d]+:[a-z-Z0-9]+$)|(?::[\S]+$))"
)


[docs]def define_ecr_session(account_id, repo_name, region, settings, role_arn=None): """ Function to determine the boto3 session to use for subsequent API calls to ECR :param account_id: :param repo_name: :param region: :param settings: :param str role_arn: :return: """ ecr_session = Session(region_name=region) current_account_id = settings.session.client("sts").get_caller_identity()["Account"] if account_id != current_account_id and role_arn is None: raise KeyError( f"The account for repository {repo_name} detected from image URI is in account " f"{account_id}, execution session in {current_account_id} and no RoleArn provided" ) elif account_id != current_account_id and role_arn: ecr_session = get_cross_role_session( settings.session, role_arn, region_name=region, session_name="ecr-scan@compose-x", ) return ecr_session
[docs]def identify_service_image(service, repo_name, image_sha, image_tag, session): """ Function to identify the image in repository that matches the one defined in service for a private ECR Based image. :param str repo_name: :param str image_sha: :param str image_tag: :param boto3.session.Session session: :return: The image definition :rtype: dict """ repo_images = list_all_images(repo_name=repo_name, ecr_session=session) for image in repo_images: if ( image_sha and keyisset("imageDigest", image) and image["imageDigest"] == image_sha ) or ( image_tag and keyisset("imageTag", image) and image["imageTag"] == image_tag ): return image else: raise LookupError( "Unable to find image", service.image.image_uri, "Deployment would result in failure.", )
[docs]def interpolate_ecr_uri_tag_with_digest(image_url, image_digest): """ Function to replace the tag from image_url :param str image_url: :param str image_digest: :return: """ tag = ECR_URI_RE.match(image_url).group("tag") if tag.startswith(r"@"): return image_url new_image = re.sub(tag, f"@{image_digest}", image_url) return new_image
[docs]def define_service_image(service, settings): """ Function to parse and identify the image for the service in AWS ECR :param ecs_composex.common.compose_services.ComposeService service: :param ecs_composex.common.settings.ComposeXSettings settings: The settings for the execution :return: """ if not service.image.private_ecr: return image_sha = None image_tag = None parts = service.image.private_ecr tag = parts.group("tag") if tag.startswith(r":"): image_tag = tag.split(":")[-1] elif tag.startswith(r"@"): image_sha = tag.split("@")[-1] repo_name = parts.group("repo_name") account_id = parts.group("account_id") region = parts.group("region") session = define_ecr_session( account_id, repo_name, region, settings, role_arn=set_else_none("RoleArn", service.x_ecr), ) the_image = identify_service_image( service, repo_name, image_sha, image_tag, session ) return the_image