Skip to content

Commit

Permalink
fix: Skip build of Docker image if ImageUri is a valid ECR URL (aws#2934
Browse files Browse the repository at this point in the history
  • Loading branch information
alexisfacques authored and moelasmar committed Aug 6, 2021
1 parent 4cbdfaf commit 4bc90d5
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 37 deletions.
2 changes: 1 addition & 1 deletion samcli/commands/deploy/guided_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ def prompt_image_repository(self, stacks: List[Stack]):
if isinstance(self.image_repositories, dict)
else "" or self.image_repository,
)
if not is_ecr_url(image_repositories.get(resource_id)):
if resource_id not in image_repositories or not is_ecr_url(str(image_repositories[resource_id])):
raise GuidedDeployFailedError(
f"Invalid Image Repository ECR URI: {image_repositories.get(resource_id)}"
)
Expand Down
2 changes: 1 addition & 1 deletion samcli/lib/package/ecr_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
from samcli.lib.package.regexpr import ECR_URL


def is_ecr_url(url):
def is_ecr_url(url: str) -> bool:
return bool(re.match(ECR_URL, url)) if url else False
28 changes: 28 additions & 0 deletions samcli/lib/providers/sam_base_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from samcli.lib.intrinsic_resolver.intrinsics_symbol_table import IntrinsicsSymbolTable
from samcli.lib.samlib.resource_metadata_normalizer import ResourceMetadataNormalizer
from samcli.lib.samlib.wrapper import SamTranslatorWrapper
from samcli.lib.package.ecr_utils import is_ecr_url


LOG = logging.getLogger(__name__)

Expand All @@ -35,6 +37,11 @@ class SamBaseProvider:
SERVERLESS_LAYER: "ContentUri",
}

IMAGE_PROPERTY_KEYS = {
LAMBDA_FUNCTION: "Code",
SERVERLESS_FUNCTION: "ImageUri",
}

def get(self, name: str) -> Optional[Any]:
"""
Given name of the function, this method must return the Function object
Expand Down Expand Up @@ -89,6 +96,17 @@ def _is_s3_location(location: Optional[Union[str, Dict]]) -> bool:
isinstance(location, str) and location.startswith("s3://")
)

@staticmethod
def _is_ecr_uri(location: Optional[Union[str, Dict]]) -> bool:
"""
the input could be:
- ImageUri of Serverless::Function
- Code of Lambda::Function
"""
return location is not None and is_ecr_url(
str(location.get("ImageUri", "")) if isinstance(location, dict) else location
)

@staticmethod
def _warn_code_extraction(resource_type: str, resource_name: str, code_property: str) -> None:
LOG.warning(
Expand All @@ -99,6 +117,16 @@ def _warn_code_extraction(resource_type: str, resource_name: str, code_property:
code_property,
)

@staticmethod
def _warn_imageuri_extraction(resource_type: str, resource_name: str, image_property: str) -> None:
LOG.warning(
"The resource %s '%s' has specified ECR registry image for %s. "
"It will not be built and SAM CLI does not support invoking it locally.",
resource_type,
resource_name,
image_property,
)

@staticmethod
def _extract_lambda_function_imageuri(resource_properties: Dict, code_property_key: str) -> Optional[str]:
"""
Expand Down
15 changes: 14 additions & 1 deletion samcli/lib/providers/sam_function_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,19 +130,32 @@ def _extract_functions(
resource_properties["Metadata"] = resource_metadata

if resource_type in [SamFunctionProvider.SERVERLESS_FUNCTION, SamFunctionProvider.LAMBDA_FUNCTION]:
resource_package_type = resource_properties.get("PackageType", ZIP)

code_property_key = SamBaseProvider.CODE_PROPERTY_KEYS[resource_type]
image_property_key = SamBaseProvider.IMAGE_PROPERTY_KEYS[resource_type]
assets = resource.assets or []
code_asset_uri = None
for asset in assets:
if isinstance(asset, S3Asset) and asset.source_property == code_property_key:
code_asset_uri = asset.source_path
break
if SamBaseProvider._is_s3_location(code_asset_uri or resource_properties.get(code_property_key)):
if resource_package_type == ZIP and SamBaseProvider._is_s3_location(
code_asset_uri or resource_properties.get(code_property_key)
):
# CodeUri can be a dictionary of S3 Bucket/Key or a S3 URI, neither of which are supported
if not ignore_code_extraction_warnings:
SamFunctionProvider._warn_code_extraction(resource_type, name, code_property_key)
continue

if resource_package_type == IMAGE and SamBaseProvider._is_ecr_uri(
resource_properties.get(image_property_key)
):
# ImageUri can be an ECR uri, which is not supported
if not ignore_code_extraction_warnings:
SamFunctionProvider._warn_imageuri_extraction(resource_type, name, image_property_key)
continue

if resource_type == SamFunctionProvider.SERVERLESS_FUNCTION:
layers = SamFunctionProvider._parse_layer_info(
stack,
Expand Down
171 changes: 137 additions & 34 deletions tests/unit/commands/local/lib/test_sam_function_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,6 @@ class TestSamFunctionProviderEndToEnd(TestCase):
"Handler": "index.handler",
},
},
"SamFunc4": {
"Type": "AWS::Serverless::Function",
"Properties": {"ImageUri": "123456789012.dkr.ecr.us-east-1.amazonaws.com/myrepo", "PackageType": IMAGE},
},
"SamFuncWithFunctionNameOverride": {
"Type": "AWS::Serverless::Function",
"Properties": {
Expand All @@ -77,6 +73,29 @@ class TestSamFunctionProviderEndToEnd(TestCase):
"Handler": "index.handler",
},
},
"SamFuncWithImage1": {
"Type": "AWS::Serverless::Function",
"Properties": {
"PackageType": IMAGE,
},
"Metadata": {"DockerTag": "tag", "DockerContext": "./image", "Dockerfile": "Dockerfile"},
},
"SamFuncWithImage2": {
"Type": "AWS::Serverless::Function",
"Properties": {
"ImageUri": "image:tag",
"PackageType": IMAGE,
},
"Metadata": {"DockerTag": "tag", "DockerContext": "./image", "Dockerfile": "Dockerfile"},
},
"SamFuncWithImage3": {
# ImageUri is unsupported ECR location
"Type": "AWS::Serverless::Function",
"Properties": {
"ImageUri": "123456789012.dkr.ecr.us-east-1.amazonaws.com/myrepo:myimage",
"PackageType": IMAGE,
},
},
"LambdaFunc1": {
"Type": "AWS::Lambda::Function",
"Properties": {
Expand All @@ -85,21 +104,37 @@ class TestSamFunctionProviderEndToEnd(TestCase):
"Handler": "index.handler",
},
},
"LambdaFuncWithInlineCode": {
"LambdaFuncWithImage1": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {"ZipFile": "testcode"},
"Runtime": "nodejs4.3",
"Handler": "index.handler",
"PackageType": IMAGE,
},
"Metadata": {"DockerTag": "tag", "DockerContext": "./image", "Dockerfile": "Dockerfile"},
},
"LambdaFunc2": {
"LambdaFuncWithImage2": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {"ImageUri": "image:tag"},
"PackageType": IMAGE,
},
"Metadata": {"DockerTag": "tag", "DockerContext": "./image", "Dockerfile": "Dockerfile"},
},
"LambdaFuncWithImage3": {
# ImageUri is unsupported ECR location
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {"ImageUri": "123456789012.dkr.ecr.us-east-1.amazonaws.com/myrepo"},
"PackageType": IMAGE,
},
},
"LambdaFuncWithInlineCode": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {"ZipFile": "testcode"},
"Runtime": "nodejs4.3",
"Handler": "index.handler",
},
},
"LambdaFuncWithLocalPath": {
"Type": "AWS::Lambda::Function",
"Properties": {"Code": "./some/path/to/code", "Runtime": "nodejs4.3", "Handler": "index.handler"},
Expand Down Expand Up @@ -251,11 +286,11 @@ def setUp(self):
("SamFunc2", None), # codeuri is a s3 location, ignored
("SamFunc3", None), # codeuri is a s3 location, ignored
(
"SamFunc4",
"SamFuncWithImage1",
Function(
function_id="SamFunc4",
name="SamFunc4",
functionname="SamFunc4",
function_id="SamFuncWithImage1",
name="SamFuncWithImage1",
functionname="SamFuncWithImage1",
runtime=None,
handler=None,
codeuri=".",
Expand All @@ -266,14 +301,47 @@ def setUp(self):
layers=[],
events=None,
inlinecode=None,
imageuri="123456789012.dkr.ecr.us-east-1.amazonaws.com/myrepo",
imageuri=None,
imageconfig=None,
packagetype=IMAGE,
metadata=None,
metadata={
"DockerTag": "tag",
"DockerContext": os.path.join("image"),
"Dockerfile": "Dockerfile",
},
codesign_config_arn=None,
stack_path="",
),
),
(
"SamFuncWithImage2",
Function(
function_id="SamFuncWithImage2",
name="SamFuncWithImage2",
functionname="SamFuncWithImage2",
runtime=None,
handler=None,
codeuri=".",
memory=None,
timeout=None,
environment=None,
rolearn=None,
layers=[],
events=None,
inlinecode=None,
imageuri="image:tag",
imageconfig=None,
packagetype=IMAGE,
metadata={
"DockerTag": "tag",
"DockerContext": os.path.join("image"),
"Dockerfile": "Dockerfile",
},
codesign_config_arn=None,
stack_path="",
),
),
("SamFuncWithImage3", None), # imageuri is ecr location, ignored
(
"SamFuncWithFunctionNameOverride-x",
Function(
Expand All @@ -300,35 +368,39 @@ def setUp(self):
),
("LambdaFunc1", None), # codeuri is a s3 location, ignored
(
"LambdaFuncWithInlineCode",
"LambdaFuncWithImage1",
Function(
function_id="LambdaFuncWithInlineCode",
name="LambdaFuncWithInlineCode",
functionname="LambdaFuncWithInlineCode",
runtime="nodejs4.3",
handler="index.handler",
codeuri=None,
function_id="LambdaFuncWithImage1",
name="LambdaFuncWithImage1",
functionname="LambdaFuncWithImage1",
runtime=None,
handler=None,
codeuri=".",
memory=None,
timeout=None,
environment=None,
rolearn=None,
layers=[],
events=None,
metadata=None,
inlinecode="testcode",
codesign_config_arn=None,
metadata={
"DockerTag": "tag",
"DockerContext": os.path.join("image"),
"Dockerfile": "Dockerfile",
},
inlinecode=None,
imageuri=None,
imageconfig=None,
packagetype=ZIP,
packagetype=IMAGE,
codesign_config_arn=None,
stack_path="",
),
),
(
"LambdaFunc2",
"LambdaFuncWithImage2",
Function(
function_id="LambdaFunc2",
name="LambdaFunc2",
functionname="LambdaFunc2",
function_id="LambdaFuncWithImage2",
name="LambdaFuncWithImage2",
functionname="LambdaFuncWithImage2",
runtime=None,
handler=None,
codeuri=".",
Expand All @@ -338,15 +410,44 @@ def setUp(self):
rolearn=None,
layers=[],
events=None,
metadata=None,
metadata={
"DockerTag": "tag",
"DockerContext": os.path.join("image"),
"Dockerfile": "Dockerfile",
},
inlinecode=None,
imageuri="123456789012.dkr.ecr.us-east-1.amazonaws.com/myrepo",
imageuri="image:tag",
imageconfig=None,
packagetype=IMAGE,
codesign_config_arn=None,
stack_path="",
),
),
("LambdaFuncWithImage3", None), # imageuri is a ecr location, ignored
(
"LambdaFuncWithInlineCode",
Function(
function_id="LambdaFuncWithInlineCode",
name="LambdaFuncWithInlineCode",
functionname="LambdaFuncWithInlineCode",
runtime="nodejs4.3",
handler="index.handler",
codeuri=None,
memory=None,
timeout=None,
environment=None,
rolearn=None,
layers=[],
events=None,
metadata=None,
inlinecode="testcode",
codesign_config_arn=None,
imageuri=None,
imageconfig=None,
packagetype=ZIP,
stack_path="",
),
),
(
"LambdaFuncWithLocalPath",
Function(
Expand Down Expand Up @@ -507,11 +608,13 @@ def test_get_all_must_return_all_functions(self):
result = {posixpath.join(f.stack_path, f.name) for f in self.provider.get_all()}
expected = {
"SamFunctions",
"SamFuncWithImage1",
"SamFuncWithImage2",
"SamFuncWithInlineCode",
"SamFunc4",
"SamFuncWithFunctionNameOverride",
"LambdaFuncWithImage1",
"LambdaFuncWithImage2",
"LambdaFuncWithInlineCode",
"LambdaFunc2",
"LambdaFuncWithLocalPath",
"LambdaFuncWithFunctionNameOverride",
"LambdaFuncWithCodeSignConfig",
Expand Down

0 comments on commit 4bc90d5

Please sign in to comment.