Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion samcli/commands/deploy/guided_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,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 @@ -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,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 @@ -98,6 +116,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