-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: sam package without awscli pre-installed #1437
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we planning on refactoring this more later? I know this is only the start but we will be incurring some tech debt with this if we keep package and deploy isolated from the rest of what we have built in this code base.
CLI command for "package" command | ||
""" | ||
|
||
from functools import partial |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the command.py
classes, we should move all imports we can into functions. We did this in other commands to help with sam --help
load time.
samcli/commands/package/command.py
Outdated
""" | ||
|
||
|
||
@click.command("package", short_help=SHORT_HELP, context_settings={"ignore_unknown_options": True}, help=HELP_TEXT) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need the context_settings here?
samcli/commands/package/command.py
Outdated
profile, | ||
): | ||
|
||
with PackageCommandContext( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I don't think you need Command in the name. For invoke and build we have InvokeContext and BuildContext.
self.kwargs = kwargs | ||
|
||
|
||
class InvalidTemplatePathError(CloudFormationCommandError): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we be combining these with our existing exceptions? We have an InvalidSamTemplateException
in samcli/commands/local/cli_common/user_exceptions.py
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
totally, needs refactoring.
@@ -0,0 +1,67 @@ | |||
""" | |||
Exceptions that are raised by sam package and sam deploy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: These exceptions are under package
but also apply to deploy. Should we have a higher level exception class that includes all user exceptions or ones shared across commands instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, needs refactoring.
msg = self.MSG_PACKAGED_TEMPLATE_WRITTEN.format( | ||
output_file_name=output_file, output_file_path=os.path.abspath(output_file) | ||
) | ||
sys.stdout.write(msg) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move to click.echo
) | ||
|
||
template_path = self.template_file | ||
if not os.path.isfile(template_path): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check isn't needed if you expand the click.Path()
or change it to click.File()
in the --template-file
option on the command.
|
||
from samcli.commands.package import exceptions | ||
from samcli.yamlhelper import yaml_dump, yaml_parse | ||
import jmespath |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New dependency? We should be adding this to our base.txt file
|
||
if isinstance(url, str) and url.startswith("s3://"): | ||
|
||
# Python < 2.7.10 don't parse query parameters from URI with custom |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there needed anymore since we have stopped support for 2.7
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch, its from the port, will remove this.
|
||
|
||
class ServerlessFunctionResource(Resource): | ||
RESOURCE_TYPE = "AWS::Serverless::Function" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the help text of the command, should we be adding what resources are supported?
How do we keep this in sync with https://github.com/awslabs/aws-sam-cli/blob/develop/samcli/commands/_utils/template.py#L19
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can do that.
Did some refactor and added the first integration test for |
- rely on boto3 instead - port over code from aws cli
f823fe7
to
1c63e19
Compare
- unit tests for package context class - removal old `test_package.py`
- add unit tests for s3 uploader
- refactor of the port from `aws-cli` for artifact exporter - add unit tests for artifact exporter
- added unit tests for the click wiring
- fix for smoke tests
- remove unneeded variables - remove unneeded exceptions, inherit package specific exceptions from UserException - refactor resources and their respective properties enums to its own file. This file is cross-referenced across package and other commands. - include jmespath as explicit dependency - refactor unit tests as required based on code structure changes.
Usage: samdev package [OPTIONS] The SAM package command creates a zip of your code and dependencies and uploads it to S3. The command returns a copy of your template, replacing references to local artifacts with the S3 location where the command uploaded the artifacts. The following resources and their property locations are supported. Resource : AWS::ServerlessRepo::Application | Location : LicenseUrl Resource : AWS::ServerlessRepo::Application | Location : ReadmeUrl Resource : AWS::Serverless::Function | Location : CodeUri Resource : AWS::Serverless::Api | Location : DefinitionUri Resource : AWS::AppSync::GraphQLSchema | Location :DefinitionS3Location Resource : AWS::AppSync::Resolver | Location : RequestMappingTemplateS3Location Resource : AWS::AppSync::Resolver | Location : ResponseMappingTemplateS3Location Resource : AWS::AppSync::FunctionConfiguration | Location : RequestMappingTemplateS3Location Resource : AWS::AppSync::FunctionConfiguration | Location : ResponseMappingTemplateS3Location Resource : AWS::Lambda::Function | Location : Code Resource : AWS::ApiGatewayRestApi | Location : BodyS3Location Resource : AWS::ElasticBeanstalk::ApplicationVersion | Location : SourceBundle Resource : AWS::CloudFormation::Stack | Location : TemplateURL Resource : AWS::Serverless::Application | Location : Location Resource : AWS::Lambda::LayerVersion | Location : Content Resource : AWS::Serverless::LayerVersion | Location : ContentUri Resource : AWS::Glue::Job | Location : Command.ScriptLocation
- add one barebones test to check `sam package` works
- With AWS::Serverless::Function Resource
Rebased to latest from develop |
- rebased on top of develop which removes python2 support
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still haven't gone through the tests. But looks really good over all.
Question:
Will this support PR add support for Globals in sam package
?
@@ -221,7 +221,7 @@ notes=FIXME,XXX | |||
[SIMILARITIES] | |||
|
|||
# Minimum lines number of a similarity. | |||
min-similarity-lines=6 | |||
min-similarity-lines=12 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
code smell alert?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
its just number of arguments being similair.
==samcli.commands.package.command:110
==samcli.commands.package.package_context:47
template_file,
s3_bucket,
s3_prefix,
kms_key_id,
output_template_file,
use_json,
force_upload,
metadata,
region,
profile,
): (duplicate-code)
samcli/settings/__init__.py:1:0: R0801: Similar lines in 2 files
==samcli.commands.package.command:96
==samcli.commands.package.package_context:47
template_file,
s3_bucket,
s3_prefix,
kms_key_id,
output_template_file,
use_json,
force_upload,
metadata, (duplicate-code)
} | ||
|
||
|
||
def resources_generator(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add doc strings for this? I having a hard time parsing what this is doing/what we would use it for
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, will add.
"AWS::Lambda::LayerVersion": ["Content"], | ||
"AWS::Serverless::LayerVersion": ["ContentUri"], | ||
} | ||
from samcli.commands._utils.resources import METADATA_WITH_LOCAL_PATHS, RESOURCES_WITH_LOCAL_PATHS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NICE!!!
Exception.__init__(self, msg) | ||
self.kwargs = kwargs | ||
|
||
fmt = "S3 Bucket does not exist. " "Execute the command to create a new bucket" "\n" "aws s3 mb s3://{bucket_name}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will \n
work on windows?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it should.
return result | ||
|
||
|
||
class ProgressPercentage: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be be using what we do for Layers instead: https://github.com/awslabs/aws-sam-cli/blob/develop/samcli/lib/utils/progressbar.py?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to investigate if click's progress bar has a callback function. Click's own Progressbar class seems to be in a private py file _term_ui_impl.py
and it also takes an iterable upfront to determine progress, which we dont know in this case. So, might need retro-fitting to use it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is publicly exposed through __init__.py
: https://github.com/pallets/click/blob/master/click/__init__.py#L36
with self._lock: | ||
self._seen_so_far += bytes_transferred | ||
percentage = (self._seen_so_far / self._size) * 100 | ||
sys.stdout.write( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be stdout or stderr?
#970
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good thought, will change to stderr
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still haven't gone through the tests. But looks really good over all.
Question:
Will this support PR add support for Globals insam package
?
In another PR. not this one.
- Additional metadata click parameter type - Added unit, integration tests for `--metadata`
dbdcf79
to
5b023d4
Compare
- Dont allow list or dict in values portion of a metadata dict
- added tests for `AWS::ServerlessRepo::Application` Metadata field
- compare output template-file from `sam package` and `aws cloudformation package`
Regression tests passing: https://gist.github.com/TheSriram/120aa167a88b17a8c2175c8bdfe04ffe |
That is what I figured but wanted to ask anyways. |
appveyor-windows.yml
Outdated
@@ -54,6 +54,8 @@ install: | |||
- "venv\\Scripts\\activate" | |||
- "python --version" | |||
|
|||
# Install AWS CLI | |||
- "pip install awscli" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this temporary until we have things ported and confident in the changes?
This will install AWS CLI into the same venv as SAM CLI. Is there any issue with that (version conflicts potentials?)? If this is not temporary, we should be moving this to its own venv and putting the cli onto the path.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, my thinking was to keep regression suite running till we have to make an explicit change in behavior in our cli wrt to the commands, installation in a separate venv does make sense though. will address that! 👍
samcli/cli/types.py
Outdated
|
||
_pattern = r"([A-Za-z0-9\"]+)=([A-Za-z0-9\"]+)" | ||
|
||
name = "" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is name used for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It isnt used for anything, but a custom type requires a name attribute, otherwise the help text crashes.
|
||
name = "" | ||
|
||
def convert(self, value, param, ctx): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add some comments above key sections here to make this method easier to understand. Looks like you are trying to parse the json way first and if that fails try the KeyName1=string, KeyName2=string2
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely, can add a docstring.
samcli/commands/_utils/resources.py
Outdated
""" | ||
for resource, locations in dict({**METADATA_WITH_LOCAL_PATHS, **RESOURCES_WITH_LOCAL_PATHS}).items(): | ||
for location in locations: | ||
yield "\nResource : {resource} | Location : {location}\n".format(resource=resource, location=location) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"\nResource : {resource} | Location : {location}\n".format(resource=resource, location=location)
could be f"\nResource : {resource} | Location : {location}\n"
🐍3!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, will change it.
|
||
def resources_generator(): | ||
""" | ||
Generator to yield set of resources and their locations that are supported for package operations |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this used for help text generation? Reading this I thought this was going to yield {(resource, location)}
but instead its a string. Just making sure I understand this correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, its a string, but I can make it yield (resource, location) tuple instead and leave the construction of the string to a different function.
SHORT_HELP = "Package an AWS SAM application." | ||
|
||
|
||
def resources_and_properties_help_string(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just have resources_generator
return the string or move the logic within resources_generator
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having a generator of resource, location would be useful elsewhere too, is my opinion.
|
||
@click.command("package", short_help=SHORT_HELP, help=HELP_TEXT, context_settings=dict(max_content_width=120)) | ||
@click.option( | ||
"--template-file", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we just be expanding the --template
option to include --template-file
as well? This would allow customers to be consistent across SAM CLI?
click.option(
"--template", "--template-file", "-t",
default=_TEMPLATE_OPTION_DEFAULT_VALUE,
type=click.Path(),
envvar="SAM_TEMPLATE_FILE",
callback=partial(get_or_default_template_file_name, include_build=include_build),
show_default=True,
help="AWS SAM template file",
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makes sense.
samcli/commands/package/command.py
Outdated
try: | ||
package_context.run() | ||
except OSError as ex: | ||
raise UserException(str(ex)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is OSError a UserException? Should we have a more specific UserException Error here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its primarily to do with Permissions on template file and output template file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the review!
0bca997
to
1d83aa7
Compare
- template can now be specified as `-t`, `--template-file` and `--template`. Future PR will standardize it across the codebase. Current commit aims to fix it only in `package` space. - exception handling on passing an unknown profile name - refactor catching of OSError within package_context.py - New exception class for package failures.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing major just a couple small comments. I did notice that all of the files are under sam/commands/package/
. We try and keep all command specific code under the sam/commands
and move all 'library' related modules to sam/lib
. artifact_exported
and s3_uploader
seem more like libs. Shouldn't we move them to follow the rest of the codebase's patterns?
appveyor-windows.yml
Outdated
@@ -49,6 +49,9 @@ install: | |||
# Upgrade setuptools, wheel and virtualenv | |||
- "python -m pip install --upgrade setuptools wheel virtualenv" | |||
|
|||
# Install AWS CLI Globally via pip3 | |||
- "pip3 install awscli" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pip3 in not on windows (according to build output)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moving back to pip.
appveyor.yml
Outdated
@@ -56,7 +59,7 @@ for: | |||
- "pylint --rcfile .pylintrc samcli" | |||
|
|||
# Runs only in Linux | |||
- sh: "pytest -vv tests/integration" | |||
- sh: "pytest -vv -n 4 tests/integration" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does this work? Are all our tests able to do this? Will this cause further issues with timeouts and other things we have been seeing?
sh:
won't run in windows. These lines can all be removed from the windows matrix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should have added this for the regression suite, not here. will move.
samcli/cli/context.py
Outdated
try: | ||
boto3.setup_default_session(region_name=self._aws_region, profile_name=self._aws_profile) | ||
except botocore.exceptions.ProfileNotFound as ex: | ||
raise UserException(str(ex)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we have more explicit exceptions? All we will see in Telemetry is UserException is thrown (which happens in a ton of places).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, can add an explicit exception.
|
||
|
||
@click.command("package", short_help=SHORT_HELP, help=HELP_TEXT, context_settings=dict(max_content_width=120)) | ||
# TODO(TheSriram): Move to template_common_option across aws-sam-cli |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just do it now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will touch the full surface of commands across build, local etc. I'd rather have a separate PR for that.
|
||
process = Popen(command_list, stdout=PIPE) | ||
process.wait() | ||
self.assertTrue(process.returncode, 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thought assertTrue
only takes in one expression? Unless the 1
is getting set to the message. Might be better to either remove 1
or make an explicit message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved to assertNotEqual to 0.
process_stdout = b"".join(process.stdout.readlines()).strip() | ||
|
||
self.assertIn( | ||
bytes( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can pass encoding into bytes builtin.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
command = [base] | ||
if os.getenv("SAM_CLI_DEV") and base == "sam": | ||
command = ["samdev"] | ||
elif base == "aws": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does aws
work on windows?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we are installing via pip, so the executable is aws.exe and should work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently we check for both .exe and .cmd for windows. https://github.com/awslabs/aws-sam-cli/blob/develop/samcli/lib/samlib/cloudformation_command.py#L43
with tempfile.NamedTemporaryFile(delete=False) as output_template_file_sam: | ||
template_path = self.test_data_path.joinpath(template_file) | ||
sam_command_list = self.get_command_list( | ||
s3_bucket=self.s3_bucket.name, | ||
template_file=template_path, | ||
output_template_file=output_template_file_sam.name, | ||
) | ||
process = Popen(sam_command_list, stdout=PIPE) | ||
process.wait() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be nice to have this abstracted. I know the regression suite is more short term, if you don't feel like it will really help in creating new tests until its removed than just ignore this comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
- move regression checking logic to base class - address comments on exceptions, appveyor file, encoding
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome work!!!
Will be making additional changes and adding tests.
Issue #, if available:
Description of changes:
Checklist:
make pr
passesBy submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.