Skip to content

Commit

Permalink
Merge branch 'release-1.16.66' into develop
Browse files Browse the repository at this point in the history
* release-1.16.66:
  Bumping version to 1.16.66
  Update changelog based on model updates
  Support for packaging LayerVersion Artifacts
  Serverless Apps Repo introduced a Resource called ServerlessApplication. Locally, this resource can point to a local template file to enable local development. The aws cloudformation package command will now support packaging of this resource's local template file and upload it to s3.
  Add --zip-file argument for lambda publish-layer-version
  • Loading branch information
aws-sdk-python-automation committed Nov 29, 2018
2 parents ab8446f + 1759356 commit 498ced2
Show file tree
Hide file tree
Showing 10 changed files with 339 additions and 40 deletions.
42 changes: 42 additions & 0 deletions .changes/1.16.66.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
[
{
"category": "``lambda``",
"description": "Update lambda command to latest version",
"type": "api-change"
},
{
"category": "``stepfunctions``",
"description": "Update stepfunctions command to latest version",
"type": "api-change"
},
{
"category": "``kafka``",
"description": "Update kafka command to latest version",
"type": "api-change"
},
{
"category": "``xray``",
"description": "Update xray command to latest version",
"type": "api-change"
},
{
"category": "``serverlessrepo``",
"description": "Update serverlessrepo command to latest version",
"type": "api-change"
},
{
"category": "``elbv2``",
"description": "Update elbv2 command to latest version",
"type": "api-change"
},
{
"category": "``events``",
"description": "Update events command to latest version",
"type": "api-change"
},
{
"category": "``s3``",
"description": "Update s3 command to latest version",
"type": "api-change"
}
]
13 changes: 13 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@
CHANGELOG
=========

1.16.66
=======

* api-change:``lambda``: Update lambda command to latest version
* api-change:``stepfunctions``: Update stepfunctions command to latest version
* api-change:``kafka``: Update kafka command to latest version
* api-change:``xray``: Update xray command to latest version
* api-change:``serverlessrepo``: Update serverlessrepo command to latest version
* api-change:``elbv2``: Update elbv2 command to latest version
* api-change:``events``: Update events command to latest version
* api-change:``s3``: Update s3 command to latest version


1.16.65
=======

Expand Down
2 changes: 1 addition & 1 deletion awscli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"""
import os

__version__ = '1.16.65'
__version__ = '1.16.66'

#
# Get our data path to be added to botocore's search path
Expand Down
104 changes: 75 additions & 29 deletions awscli/customizations/awslambda.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,23 @@
from botocore.vendored import six

from awscli.arguments import CustomArgument, CLIArgument
from awscli.customizations import utils


ERROR_MSG = (
"--zip-file must be a zip file with the fileb:// prefix.\n"
"Example usage: --zip-file fileb://path/to/file.zip")

ZIP_DOCSTRING = ('<p>The path to the zip file of the code you are uploading. '
'Example: fileb://code.zip</p>')
ZIP_DOCSTRING = (
'<p>The path to the zip file of the {param_type} you are uploading. '
'Example: fileb://{param_type}.zip</p>'
)


def register_lambda_create_function(cli):
cli.register('building-argument-table.lambda.create-function',
_extract_code_and_zip_file_arguments)
ZipFileArgumentHoister('Code').hoist)
cli.register('building-argument-table.lambda.publish-layer-version',
ZipFileArgumentHoister('Content').hoist)
cli.register('building-argument-table.lambda.update-function-code',
_modify_zipfile_docstring)
cli.register('process-cli-arg.lambda.update-function-code',
Expand All @@ -41,20 +45,36 @@ def validate_is_zip_file(cli_argument, value, **kwargs):
_should_contain_zip_content(value)


def _extract_code_and_zip_file_arguments(session, argument_table, **kwargs):
argument_table['zip-file'] = ZipFileArgument(
'zip-file', help_text=ZIP_DOCSTRING, cli_type_name='blob')
code_argument = argument_table['code']
code_model = copy.deepcopy(code_argument.argument_model)
del code_model.members['ZipFile']
argument_table['code'] = CodeArgument(
name='code',
argument_model=code_model,
operation_model=code_argument._operation_model,
is_required=False,
event_emitter=session.get_component('event_emitter'),
serialized_name='Code'
)
class ZipFileArgumentHoister(object):
"""Hoists a ZipFile argument up to the top level.
Injects a top-level ZipFileArgument into the argument table which maps
a --zip-file parameter to the underlying ``serialized_name`` ZipFile
shape. Repalces the old ZipFile argument with an instance of
ReplacedZipFileArgument to prevent its usage and recommend the new
top-level injected parameter.
"""
def __init__(self, serialized_name):
self._serialized_name = serialized_name
self._name = serialized_name.lower()

def hoist(self, session, argument_table, **kwargs):
help_text = ZIP_DOCSTRING.format(param_type=self._name)
argument_table['zip-file'] = ZipFileArgument(
'zip-file', help_text=help_text, cli_type_name='blob',
serialized_name=self._serialized_name
)
argument = argument_table[self._name]
model = copy.deepcopy(argument.argument_model)
del model.members['ZipFile']
argument_table[self._name] = ReplacedZipFileArgument(
name=self._name,
argument_model=model,
operation_model=argument._operation_model,
is_required=False,
event_emitter=session.get_component('event_emitter'),
serialized_name=self._serialized_name,
)


def _modify_zipfile_docstring(session, argument_table, **kwargs):
Expand All @@ -78,28 +98,54 @@ def _should_contain_zip_content(value):


class ZipFileArgument(CustomArgument):
"""A new ZipFile argument to be injected at the top level.
This class injects a ZipFile argument under the specified serialized_name
parameter. This can be used to take a top level parameter like --zip-file
and inject it into a nested different parameter like Code so
--zip-file foo.zip winds up being serilized as
{ 'Code': { 'ZipFile': <contents of foo.zip> } }.
"""
def __init__(self, *args, **kwargs):
self._param_to_replace = kwargs.pop('serialized_name')
super(ZipFileArgument, self).__init__(*args, **kwargs)

def add_to_params(self, parameters, value):
if value is None:
return
_should_contain_zip_content(value)
zip_file_param = {'ZipFile': value}
if parameters.get('Code'):
parameters['Code'].update(zip_file_param)
if parameters.get(self._param_to_replace):
parameters[self._param_to_replace].update(zip_file_param)
else:
parameters['Code'] = zip_file_param
parameters[self._param_to_replace] = zip_file_param


class ReplacedZipFileArgument(CLIArgument):
"""A replacement arugment for nested ZipFile argument.
This prevents the use of a non-working nested argument that expects binary.
Instead an instance of ZipFileArgument should be injected at the top level
and used instead. That way fileb:// can be used to load the binary
contents. And the argument class can inject those bytes into the correct
serialization name.
"""
def __init__(self, *args, **kwargs):
super(ReplacedZipFileArgument, self).__init__(*args, **kwargs)
self._cli_name = '--%s' % kwargs['name']
self._param_to_replace = kwargs['serialized_name']

class CodeArgument(CLIArgument):
def add_to_params(self, parameters, value):
if value is None:
return
unpacked = self._unpack_argument(value)
if 'ZipFile' in unpacked:
raise ValueError("ZipFile cannot be provided "
"as part of the --code argument. "
"Please use the '--zip-file' "
"option instead to specify a zip file.")
if parameters.get('Code'):
parameters['Code'].update(unpacked)
raise ValueError(
"ZipFile cannot be provided "
"as part of the %s argument. "
"Please use the '--zip-file' "
"option instead to specify a zip file." % self._cli_name)
if parameters.get(self._param_to_replace):
parameters[self._param_to_replace].update(unpacked)
else:
parameters['Code'] = unpacked
parameters[self._param_to_replace] = unpacked
32 changes: 29 additions & 3 deletions awscli/customizations/cloudformation/artifact_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,21 @@ class ElasticBeanstalkApplicationVersion(ResourceWithS3UrlDict):
VERSION_PROPERTY = None


class LambdaLayerVersionResource(ResourceWithS3UrlDict):
RESOURCE_TYPE = "AWS::Lambda::LayerVersion"
PROPERTY_NAME = "Content"
BUCKET_NAME_PROPERTY = "S3Bucket"
OBJECT_KEY_PROPERTY = "S3Key"
VERSION_PROPERTY = "S3ObjectVersion"
FORCE_ZIP = True


class ServerlessLayerVersionResource(Resource):
RESOURCE_TYPE = "AWS::Serverless::LayerVersion"
PROPERTY_NAME = "ContentUri"
FORCE_ZIP = True


class CloudFormationStackResource(Resource):
"""
Represents CloudFormation::Stack resource that can refer to a nested
Expand Down Expand Up @@ -420,6 +435,15 @@ def do_export(self, resource_id, resource_dict, parent_dir):
parts["Key"], parts.get("Version", None))


class ServerlessApplicationResource(CloudFormationStackResource):
"""
Represents Serverless::Application resource that can refer to a nested
app template via Location property.
"""
RESOURCE_TYPE = "AWS::Serverless::Application"
PROPERTY_NAME = "Location"


EXPORT_LIST = [
ServerlessFunctionResource,
ServerlessApiResource,
Expand All @@ -429,7 +453,9 @@ def do_export(self, resource_id, resource_dict, parent_dir):
ApiGatewayRestApiResource,
LambdaFunctionResource,
ElasticBeanstalkApplicationVersion,
CloudFormationStackResource
CloudFormationStackResource,
ServerlessApplicationResource,
LambdaLayerVersionResource,
]

def include_transform_export_handler(template_dict, uploader):
Expand Down Expand Up @@ -474,9 +500,9 @@ def __init__(self, template_path, parent_dir, uploader,

def export_global_artifacts(self, template_dict):
"""
Template params such as AWS::Include transforms are not specific to
Template params such as AWS::Include transforms are not specific to
any resource type but contain artifacts that should be exported,
here we iterate through the template dict and export params with a
here we iterate through the template dict and export params with a
handler defined in GLOBAL_EXPORT_DICT
"""
for key, val in template_dict.items():
Expand Down
2 changes: 1 addition & 1 deletion doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
# The short X.Y version.
version = '1.16.'
# The full version, including alpha/beta/rc tags.
release = '1.16.65'
release = '1.16.66'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ universal = 1

[metadata]
requires-dist =
botocore==1.12.55
botocore==1.12.56
colorama>=0.2.5,<=0.3.9
docutils>=0.10
rsa>=3.1.2,<=3.5.0
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def find_version(*file_paths):
raise RuntimeError("Unable to find version string.")


requires = ['botocore==1.12.55',
requires = ['botocore==1.12.56',
'colorama>=0.2.5,<=0.3.9',
'docutils>=0.10',
'rsa>=3.1.2,<=3.5.0',
Expand Down
74 changes: 72 additions & 2 deletions tests/functional/awslambda/test_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import zipfile
from contextlib import closing

from awscli.testutils import unittest
from awscli.testutils import BaseAWSCommandParamsTest
from awscli.testutils import FileCreator

Expand Down Expand Up @@ -121,6 +120,78 @@ def test_not_using_fileb_prefix(self):
self.assertIn('fileb://', stderr)


class TestPublishLayerVersion(BaseLambdaTests):

prefix = 'lambda publish-layer-version'

def test_publish_layer_version_with_file(self):
cmdline = self.prefix
cmdline += ' --layer-name mylayer'
cmdline += ' --zip-file fileb://%s' % self.zip_file
result = {
'LayerName': 'mylayer',
'Content': {'ZipFile': self.zip_file_contents}
}
self.assert_params_for_cmd(cmdline, result)

def test_publish_layer_version_with_content_argument(self):
cmdline = self.prefix
cmdline += ' --layer-name mylayer'
cmdline += ' --content'
cmdline += ' S3Bucket=mybucket,S3Key=mykey,S3ObjectVersion=vs'
result = {
'LayerName': 'mylayer',
'Content': {'S3Bucket': 'mybucket',
'S3Key': 'mykey',
'S3ObjectVersion': 'vs'}
}
self.assert_params_for_cmd(cmdline, result)

def test_publish_layer_version_with_content_and_zipfile_argument(self):
cmdline = self.prefix
cmdline += ' --layer-name mylayer'
cmdline += ' --content'
cmdline += ' S3Bucket=mybucket,S3Key=mykey,S3ObjectVersion=vs'
cmdline += ' --zip-file fileb://%s' % self.zip_file
result = {
'LayerName': 'mylayer',
'Content': {'S3Bucket': 'mybucket',
'S3Key': 'mykey',
'S3ObjectVersion': 'vs',
'ZipFile': self.zip_file_contents}
}
self.assert_params_for_cmd(cmdline, result)

def test_publish_layer_version_with_zip_file_in_content_argument(self):
cmdline = self.prefix
cmdline += ' --layer-name mylayer'
cmdline += ' --content'
cmdline += ' S3Bucket=mybucket,S3Key=mykey,S3ObjectVersion=vs,'
cmdline += 'ZipFile=foo'
stdout, stderr, rc = self.run_cmd(cmdline, expected_rc=255)
self.assertIn('ZipFile cannot be provided as part of the --content',
stderr)

def test_publish_layer_version_with_invalid_file_contents(self):
cmdline = self.prefix
cmdline += ' --layer-name mylayer'
cmdline += ' --zip-file filename_instead_of_contents.zip'
stdout, stderr, rc = self.run_cmd(cmdline, expected_rc=255)
self.assertIn('must be a zip file with the fileb:// prefix', stderr)
# Should also give a pointer to fileb:// for them.
self.assertIn('fileb://', stderr)

def test_not_using_fileb_prefix(self):
cmdline = self.prefix
cmdline += ' --layer-name mylayer'
# Note file:// instead of fileb://
cmdline += ' --zip-file file://%s' % self.zip_file
stdout, stderr, rc = self.run_cmd(cmdline, expected_rc=255)
# Ensure we mention fileb:// to give the user an idea of
# where to go next.
self.assertIn('fileb://', stderr)


class TestUpdateFunctionCode(BaseLambdaTests):

prefix = 'lambda update-function-code'
Expand All @@ -142,4 +213,3 @@ def test_using_fileb_prefix_succeeds(self):
'ZipFile': self.zip_file_contents,
}
self.assert_params_for_cmd(cmdline, result)

Loading

0 comments on commit 498ced2

Please sign in to comment.