Skip to content

Commit

Permalink
fix: Skip build of Docker image if ImageUri is a valid ECR URL (#2934)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexisfacques committed Jun 21, 2021
1 parent 3c54e78 commit 76bc6f9
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 32 deletions.
26 changes: 26 additions & 0 deletions samcli/lib/providers/sam_base_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,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 @@ -34,6 +36,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 @@ -88,6 +95,15 @@ 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 is_ecr_url(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 @@ -98,6 +114,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
17 changes: 16 additions & 1 deletion samcli/lib/providers/sam_function_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,28 @@ 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]
if SamBaseProvider._is_s3_location(resource_properties.get(code_property_key)):
image_property_key = SamBaseProvider.IMAGE_PROPERTY_KEYS[resource_type]

if resource_package_type == ZIP and SamBaseProvider._is_s3_location(
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
163 changes: 132 additions & 31 deletions tests/unit/commands/local/lib/test_sam_function_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,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 @@ -76,6 +72,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 @@ -84,21 +103,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"},
},
"LambdaFuncWithImage2": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {"ImageUri": "image:tag"},
"PackageType": IMAGE,
},
"Metadata": {"DockerTag": "tag", "DockerContext": "./image", "Dockerfile": "Dockerfile"},
},
"LambdaFunc2": {
"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 @@ -248,10 +283,10 @@ def setUp(self):
("SamFunc2", None), # codeuri is a s3 location, ignored
("SamFunc3", None), # codeuri is a s3 location, ignored
(
"SamFunc4",
"SamFuncWithImage1",
Function(
name="SamFunc4",
functionname="SamFunc4",
name="SamFuncWithImage1",
functionname="SamFuncWithImage1",
runtime=None,
handler=None,
codeuri=".",
Expand All @@ -262,14 +297,46 @@ 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(
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 @@ -295,33 +362,37 @@ def setUp(self):
),
("LambdaFunc1", None), # codeuri is a s3 location, ignored
(
"LambdaFuncWithInlineCode",
"LambdaFuncWithImage1",
Function(
name="LambdaFuncWithInlineCode",
functionname="LambdaFuncWithInlineCode",
runtime="nodejs4.3",
handler="index.handler",
codeuri=None,
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(
name="LambdaFunc2",
functionname="LambdaFunc2",
name="LambdaFuncWithImage2",
functionname="LambdaFuncWithImage2",
runtime=None,
handler=None,
codeuri=".",
Expand All @@ -331,15 +402,43 @@ 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(
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 @@ -494,11 +593,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 76bc6f9

Please sign in to comment.