Skip to content

Commit

Permalink
sam init - Enable --app-template argument for Image package-type (aws…
Browse files Browse the repository at this point in the history
…#2864)

* Enable --app-template argument for Image package-type while generating a new SAM project using 'sam init'

* Fix the exception message

* normalize pathes in UT to pass on windows

* normalize project-template local path
  • Loading branch information
elbayaaa authored and moelasmar committed Jul 1, 2021
1 parent dd3c862 commit 0a0b9c7
Show file tree
Hide file tree
Showing 3 changed files with 261 additions and 11 deletions.
19 changes: 10 additions & 9 deletions samcli/commands/init/__init__.py
Expand Up @@ -163,7 +163,7 @@ def wrapped(*args, **kwargs):
default=None,
help="Lambda Image of your app",
cls=Mutex,
not_required=["location", "app_template", "runtime"],
not_required=["location", "runtime"],
)
@click.option(
"-d",
Expand All @@ -182,7 +182,7 @@ def wrapped(*args, **kwargs):
help="Identifier of the managed application template you want to use. "
"If not sure, call 'sam init' without options for an interactive workflow.",
cls=Mutex,
not_required=["location", "base_image"],
not_required=["location"],
)
@click.option(
"--no-input",
Expand Down Expand Up @@ -277,13 +277,14 @@ def do_cli(
if package_type == IMAGE and image_bool:
base_image, runtime = _get_runtime_from_image(base_image)
options = templates.init_options(package_type, runtime, base_image, dependency_manager)
if len(options) == 1:
app_template = options[0].get("appTemplate")
elif len(options) > 1:
raise LambdaImagesTemplateException(
"Multiple lambda image application templates found. "
"This should not be possible, please raise an issue."
)
if not app_template:
if len(options) == 1:
app_template = options[0].get("appTemplate")
elif len(options) > 1:
raise LambdaImagesTemplateException(
"Multiple lambda image application templates found. "
"Please specify one using the --app-template parameter."
)

if app_template and not location:
location = templates.location_from_app_template(
Expand Down
2 changes: 1 addition & 1 deletion samcli/commands/init/init_templates.py
Expand Up @@ -91,7 +91,7 @@ def location_from_app_template(self, package_type, runtime, base_image, dependen
if template.get("init_location") is not None:
return template["init_location"]
if template.get("directory") is not None:
return os.path.join(self._git_repo.local_path, template["directory"])
return os.path.normpath(os.path.join(self._git_repo.local_path, template["directory"]))
raise InvalidInitTemplateError("Invalid template. This should not be possible, please raise an issue.")
except StopIteration as ex:
msg = "Can't find application template " + app_template + " - check valid values in interactive init."
Expand Down
251 changes: 250 additions & 1 deletion tests/unit/commands/init/test_cli.py
@@ -1,3 +1,4 @@
import os
from pathlib import Path
from unittest import TestCase
from unittest.mock import patch, ANY
Expand All @@ -9,7 +10,7 @@
from samcli.commands.exceptions import UserException
from samcli.commands.init import cli as init_cmd
from samcli.commands.init import do_cli as init_cli
from samcli.commands.init.init_templates import InitTemplates, APP_TEMPLATES_REPO_URL, APP_TEMPLATES_REPO_NAME
from samcli.commands.init.init_templates import InitTemplates, APP_TEMPLATES_REPO_URL
from samcli.lib.init import GenerateProjectFailedError
from samcli.lib.utils.git_repo import GitRepo
from samcli.lib.utils.packagetype import IMAGE, ZIP
Expand Down Expand Up @@ -1101,3 +1102,251 @@ def test_init_cli_no_package_type(self, generate_project_patch, cd_mock):
True,
ANY,
)

@patch.object(InitTemplates, "__init__", MockInitTemplates.__init__)
@patch("samcli.commands.init.init_templates.InitTemplates._init_options_from_manifest")
def test_init_cli_image_pool_with_base_image_having_multiple_managed_template_but_no_app_template_provided(
self,
init_options_from_manifest_mock,
):
init_options_from_manifest_mock.return_value = [
{
"directory": "python3.8-image/cookiecutter-aws-sam-hello-python-lambda-image",
"displayName": "Hello World Lambda Image Example",
"dependencyManager": "pip",
"appTemplate": "hello-world-lambda-image",
"packageType": "Image",
},
{
"directory": "python3.8-image/cookiecutter-ml-apigw-pytorch",
"displayName": "PyTorch Machine Learning Inference API",
"dependencyManager": "pip",
"appTemplate": "ml-apigw-pytorch",
"packageType": "Image",
},
]
with self.assertRaises(UserException):
init_cli(
ctx=self.ctx,
no_interactive=self.no_interactive,
pt_explicit=self.pt_explicit,
package_type="Image",
base_image="amazon/python3.8-base",
dependency_manager="pip",
app_template=None,
name=self.name,
output_dir=self.output_dir,
location=None,
runtime=None,
no_input=self.no_input,
extra_context=self.extra_context,
)

@patch.object(InitTemplates, "__init__", MockInitTemplates.__init__)
@patch("samcli.commands.init.init_templates.InitTemplates._init_options_from_manifest")
def test_init_cli_image_pool_with_base_image_having_multiple_managed_template_and_provided_app_template_not_matching_any_managed_templates(
self,
init_options_from_manifest_mock,
):
init_options_from_manifest_mock.return_value = [
{
"directory": "python3.8-image/cookiecutter-aws-sam-hello-python-lambda-image",
"displayName": "Hello World Lambda Image Example",
"dependencyManager": "pip",
"appTemplate": "hello-world-lambda-image",
"packageType": "Image",
},
{
"directory": "python3.8-image/cookiecutter-ml-apigw-pytorch",
"displayName": "PyTorch Machine Learning Inference API",
"dependencyManager": "pip",
"appTemplate": "ml-apigw-pytorch",
"packageType": "Image",
},
]
with self.assertRaises(UserException):
init_cli(
ctx=self.ctx,
no_interactive=self.no_interactive,
pt_explicit=self.pt_explicit,
package_type="Image",
base_image="amazon/python3.8-base",
dependency_manager="pip",
app_template="Not-ml-apigw-pytorch", # different value than appTemplates shown in the manifest above
name=self.name,
output_dir=self.output_dir,
location=None,
runtime=None,
no_input=self.no_input,
extra_context=self.extra_context,
)

@patch.object(InitTemplates, "__init__", MockInitTemplates.__init__)
@patch("samcli.commands.init.init_templates.InitTemplates._init_options_from_manifest")
@patch("samcli.commands.init.init_generator.generate_project")
def test_init_cli_image_pool_with_base_image_having_multiple_managed_template_with_matching_app_template_provided(
self,
generate_project_patch,
init_options_from_manifest_mock,
):
init_options_from_manifest_mock.return_value = [
{
"directory": "python3.8-image/cookiecutter-aws-sam-hello-python-lambda-image",
"displayName": "Hello World Lambda Image Example",
"dependencyManager": "pip",
"appTemplate": "hello-world-lambda-image",
"packageType": "Image",
},
{
"directory": "python3.8-image/cookiecutter-ml-apigw-pytorch",
"displayName": "PyTorch Machine Learning Inference API",
"dependencyManager": "pip",
"appTemplate": "ml-apigw-pytorch",
"packageType": "Image",
},
]
init_cli(
ctx=self.ctx,
no_interactive=True,
pt_explicit=True,
package_type="Image",
base_image="amazon/python3.8-base",
dependency_manager="pip",
app_template="ml-apigw-pytorch", # same value as one appTemplate in the manifest above
name=self.name,
output_dir=None,
location=None,
runtime=None,
no_input=None,
extra_context=None,
)
generate_project_patch.assert_called_once_with(
os.path.normpath("repository/python3.8-image/cookiecutter-ml-apigw-pytorch"), # location
"Image", # package_type
"python3.8", # runtime
"pip", # dependency_manager
self.output_dir,
self.name,
True, # no_input
ANY,
)

@patch.object(InitTemplates, "__init__", MockInitTemplates.__init__)
@patch("samcli.commands.init.init_templates.InitTemplates._init_options_from_manifest")
@patch("samcli.commands.init.init_generator.generate_project")
def test_init_cli_image_pool_with_base_image_having_one_managed_template_does_not_need_app_template_argument(
self,
generate_project_patch,
init_options_from_manifest_mock,
):
init_options_from_manifest_mock.return_value = [
{
"directory": "python3.8-image/cookiecutter-ml-apigw-pytorch",
"displayName": "PyTorch Machine Learning Inference API",
"dependencyManager": "pip",
"appTemplate": "ml-apigw-pytorch",
"packageType": "Image",
},
]
init_cli(
ctx=self.ctx,
no_interactive=True,
pt_explicit=True,
package_type="Image",
base_image="amazon/python3.8-base",
dependency_manager="pip",
app_template=None,
name=self.name,
output_dir=None,
location=None,
runtime=None,
no_input=None,
extra_context=None,
)
generate_project_patch.assert_called_once_with(
os.path.normpath("repository/python3.8-image/cookiecutter-ml-apigw-pytorch"), # location
"Image", # package_type
"python3.8", # runtime
"pip", # dependency_manager
self.output_dir,
self.name,
True, # no_input
ANY,
)

@patch.object(InitTemplates, "__init__", MockInitTemplates.__init__)
@patch("samcli.commands.init.init_templates.InitTemplates._init_options_from_manifest")
@patch("samcli.commands.init.init_generator.generate_project")
def test_init_cli_image_pool_with_base_image_having_one_managed_template_with_provided_app_template_matching_the_managed_template(
self,
generate_project_patch,
init_options_from_manifest_mock,
):
init_options_from_manifest_mock.return_value = [
{
"directory": "python3.8-image/cookiecutter-ml-apigw-pytorch",
"displayName": "PyTorch Machine Learning Inference API",
"dependencyManager": "pip",
"appTemplate": "ml-apigw-pytorch",
"packageType": "Image",
},
]
init_cli(
ctx=self.ctx,
no_interactive=True,
pt_explicit=True,
package_type="Image",
base_image="amazon/python3.8-base",
dependency_manager="pip",
app_template="ml-apigw-pytorch", # same value as appTemplate indicated in the manifest above
name=self.name,
output_dir=None,
location=None,
runtime=None,
no_input=None,
extra_context=None,
)
generate_project_patch.assert_called_once_with(
os.path.normpath("repository/python3.8-image/cookiecutter-ml-apigw-pytorch"), # location
"Image", # package_type
"python3.8", # runtime
"pip", # dependency_manager
self.output_dir,
self.name,
True, # no_input
ANY,
)

@patch.object(InitTemplates, "__init__", MockInitTemplates.__init__)
@patch("samcli.commands.init.init_templates.InitTemplates._init_options_from_manifest")
@patch("samcli.commands.init.init_generator.generate_project")
def test_init_cli_image_pool_with_base_image_having_one_managed_template_with_provided_app_template_not_matching_the_managed_template(
self,
generate_project_patch,
init_options_from_manifest_mock,
):
init_options_from_manifest_mock.return_value = [
{
"directory": "python3.8-image/cookiecutter-ml-apigw-pytorch",
"displayName": "PyTorch Machine Learning Inference API",
"dependencyManager": "pip",
"appTemplate": "ml-apigw-pytorch",
"packageType": "Image",
},
]
with (self.assertRaises(UserException)):
init_cli(
ctx=self.ctx,
no_interactive=True,
pt_explicit=True,
package_type="Image",
base_image="amazon/python3.8-base",
dependency_manager="pip",
app_template="NOT-ml-apigw-pytorch", # different value than appTemplate shown in the manifest above
name=self.name,
output_dir=None,
location=None,
runtime=None,
no_input=None,
extra_context=None,
)

0 comments on commit 0a0b9c7

Please sign in to comment.