Skip to content

Commit

Permalink
Merge pull request #882 from awslabs/develop
Browse files Browse the repository at this point in the history
chore: Release v0.10.0
  • Loading branch information
sriram-mv committed Dec 21, 2018
2 parents 7e08351 + f6f55bb commit afce048
Show file tree
Hide file tree
Showing 46 changed files with 1,281 additions and 156 deletions.
5 changes: 3 additions & 2 deletions requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ docker>=3.3.0
dateparser~=0.7
python-dateutil~=2.6
pathlib2~=2.3.2; python_version<"3.4"
requests~=2.20.0
aws_lambda_builders==0.0.4
requests==2.20.1
aws_lambda_builders==0.0.5
serverlessrepo==0.1.5
1 change: 0 additions & 1 deletion requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ pylint==1.7.2
pytest==3.0.7
py==1.4.33
mock==2.0.0
requests==2.20.0
parameterized==0.6.1
pathlib2==2.3.2; python_version<"3.4"
futures==3.2.0; python_version<"3.2.3"
Expand Down
2 changes: 1 addition & 1 deletion samcli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
SAM CLI version
"""

__version__ = '0.9.0'
__version__ = '0.10.0'
3 changes: 2 additions & 1 deletion samcli/cli/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"samcli.commands.deploy",
"samcli.commands.package",
"samcli.commands.logs",
"samcli.commands.build"
"samcli.commands.build",
"samcli.commands.publish"
}


Expand Down
31 changes: 19 additions & 12 deletions samcli/commands/local/cli_common/invoke_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import os

import samcli.lib.utils.osutils as osutils
from samcli.lib.utils.stream_writer import StreamWriter
from samcli.commands.local.lib.local_lambda import LocalLambdaRunner
from samcli.commands.local.lib.debug_context import DebugContext
from samcli.local.lambdafn.runtime import LambdaRuntime
Expand Down Expand Up @@ -202,26 +203,28 @@ def local_lambda_runner(self):
@property
def stdout(self):
"""
Returns a stdout stream to output Lambda function logs to
Returns stream writer for stdout to output Lambda function logs to
:return File like object: Stream where the output of the function is sent to
Returns
-------
samcli.lib.utils.stream_writer.StreamWriter
Stream writer for stdout
"""
if self._log_file_handle:
return self._log_file_handle

return osutils.stdout()
stream = self._log_file_handle if self._log_file_handle else osutils.stdout()
return StreamWriter(stream, self._is_debugging)

@property
def stderr(self):
"""
Returns stderr stream to output Lambda function errors to
Returns stream writer for stderr to output Lambda function errors to
:return File like object: Stream where the stderr of the function is sent to
Returns
-------
samcli.lib.utils.stream_writer.StreamWriter
Stream writer for stderr
"""
if self._log_file_handle:
return self._log_file_handle

return osutils.stderr()
stream = self._log_file_handle if self._log_file_handle else osutils.stderr()
return StreamWriter(stream, self._is_debugging)

@property
def template(self):
Expand Down Expand Up @@ -256,6 +259,10 @@ def parameter_overrides(self):

return self._parameter_overrides

@property
def _is_debugging(self):
return bool(self._debug_context)

@staticmethod
def _get_template_data(template_file):
"""
Expand Down
3 changes: 0 additions & 3 deletions samcli/commands/local/lib/debug_context.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""
Information and debug options for a specific runtime.
"""
import os


class DebugContext(object):
Expand All @@ -14,8 +13,6 @@ def __init__(self,
self.debug_port = debug_port
self.debugger_path = debugger_path
self.debug_args = debug_args
if self.debug_port:
os.environ["PYTHONUNBUFFERED"] = "1"

def __bool__(self):
return bool(self.debug_port)
Expand Down
20 changes: 15 additions & 5 deletions samcli/commands/local/lib/local_lambda.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,21 @@ def invoke(self, function_name, event, stdout=None, stderr=None):
This function will block until either the function completes or times out.
:param string function_name: Name of the Lambda function to invoke
:param string event: Event data passed to the function. Must be a valid JSON String.
:param io.BaseIO stdout: Stream to write the output of the Lambda function to.
:param io.BaseIO stderr: Stream to write the Lambda runtime logs to.
:raises FunctionNotfound: When we cannot find a function with the given name
Parameters
----------
function_name str
Name of the Lambda function to invoke
event str
Event data passed to the function. Must be a valid JSON String.
stdout samcli.lib.utils.stream_writer.StreamWriter
Stream writer to write the output of the Lambda function to.
stderr samcli.lib.utils.stream_writer.StreamWriter
Stream writer to write the Lambda runtime logs to.
Raises
------
FunctionNotfound
When we cannot find a function with the given name
"""

# Generate the correct configuration based on given inputs
Expand Down
4 changes: 4 additions & 0 deletions samcli/commands/publish/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"""`sam publish` command."""

# Expose the cli object here
from .command import cli # noqa
135 changes: 135 additions & 0 deletions samcli/commands/publish/command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
"""CLI command for "publish" command."""

import json

import click
import boto3
from botocore.exceptions import ClientError
from serverlessrepo import publish_application
from serverlessrepo.publish import CREATE_APPLICATION
from serverlessrepo.exceptions import ServerlessRepoError

from samcli.cli.main import pass_context, common_options as cli_framework_options, aws_creds_options
from samcli.commands._utils.options import template_common_option
from samcli.commands._utils.template import get_template_data
from samcli.commands.exceptions import UserException

HELP_TEXT = """
Use this command to publish a packaged AWS SAM template to
the AWS Serverless Application Repository to share within your team,
across your organization, or with the community at large.\n
\b
This command expects the template's Metadata section to contain an
AWS::ServerlessRepo::Application section with application metadata
for publishing. For more details on this metadata section, see
https://docs.aws.amazon.com/serverlessrepo/latest/devguide/serverless-app-publishing-applications.html
\b
Examples
--------
To publish an application
$ sam publish -t packaged.yaml --region <region>
"""
SHORT_HELP = "Publish a packaged AWS SAM template to the AWS Serverless Application Repository."
SERVERLESSREPO_CONSOLE_URL = "https://console.aws.amazon.com/serverlessrepo/home?region={}#/published-applications/{}"


@click.command("publish", help=HELP_TEXT, short_help=SHORT_HELP)
@template_common_option
@aws_creds_options
@cli_framework_options
@pass_context
def cli(ctx, template):
# All logic must be implemented in the ``do_cli`` method. This helps with easy unit testing

do_cli(ctx, template) # pragma: no cover


def do_cli(ctx, template):
"""Publish the application based on command line inputs."""
try:
template_data = get_template_data(template)
except ValueError as ex:
click.secho("Publish Failed", fg='red')
raise UserException(str(ex))

try:
publish_output = publish_application(template_data)
click.secho("Publish Succeeded", fg="green")
click.secho(_gen_success_message(publish_output), fg="yellow")
except ServerlessRepoError as ex:
click.secho("Publish Failed", fg='red')
raise UserException(str(ex))
except ClientError as ex:
click.secho("Publish Failed", fg='red')
raise _wrap_s3_uri_exception(ex)

application_id = publish_output.get('application_id')
_print_console_link(ctx.region, application_id)


def _gen_success_message(publish_output):
"""
Generate detailed success message for published applications.
Parameters
----------
publish_output : dict
Output from serverlessrepo publish_application
Returns
-------
str
Detailed success message
"""
application_id = publish_output.get('application_id')
details = json.dumps(publish_output.get('details'), indent=2)

if CREATE_APPLICATION in publish_output.get('actions'):
return "Created new application with the following metadata:\n{}".format(details)

return 'The following metadata of application "{}" has been updated:\n{}'.format(application_id, details)


def _print_console_link(region, application_id):
"""
Print link for the application in AWS Serverless Application Repository console.
Parameters
----------
region : str
AWS region name
application_id : str
The Amazon Resource Name (ARN) of the application
"""
if not region:
region = boto3.Session().region_name

console_link = SERVERLESSREPO_CONSOLE_URL.format(region, application_id.replace('/', '~'))
msg = "Click the link below to view your application in AWS console:\n{}".format(console_link)
click.secho(msg, fg="yellow")


def _wrap_s3_uri_exception(ex):
"""
Wrap invalid S3 URI exception with a better error message.
Parameters
----------
ex : ClientError
boto3 exception
Returns
-------
Exception
UserException if found invalid S3 URI or ClientError
"""
error_code = ex.response.get('Error').get('Code')
message = ex.response.get('Error').get('Message')

if error_code == 'BadRequestException' and "Invalid S3 URI" in message:
return UserException(
"Your SAM template contains invalid S3 URIs. Please make sure that you have uploaded application "
"artifacts to S3 by packaging the template: 'sam package --template-file <file-path>'.")

return ex
6 changes: 6 additions & 0 deletions samcli/lib/build/app_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ def _get_workflow_config(runtime):
dependency_manager="npm",
application_framework=None,
manifest_name="package.json")
elif runtime.startswith("ruby"):
return config(
language="ruby",
dependency_manager="bundler",
application_framework=None,
manifest_name="Gemfile")
else:
raise UnsupportedRuntimeException("'{}' runtime is not supported".format(runtime))

Expand Down
37 changes: 37 additions & 0 deletions samcli/lib/utils/stream_writer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""
This class acts like a wrapper around output streams to provide any flexibility with output we need
"""


class StreamWriter(object):

def __init__(self, stream, auto_flush=False):
"""
Instatiates new StreamWriter to the specified stream
Parameters
----------
stream io.RawIOBase
Stream to wrap
auto_flush bool
Whether to autoflush the stream upon writing
"""
self._stream = stream
self._auto_flush = auto_flush

def write(self, output):
"""
Writes specified text to the underlying stream
Parameters
----------
output bytes-like object
Bytes to write
"""
self._stream.write(output)

if self._auto_flush:
self._stream.flush()

def flush(self):
self._stream.flush()
41 changes: 27 additions & 14 deletions samcli/local/apigw/local_apigw_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from flask import Flask, request

from samcli.local.services.base_local_service import BaseLocalService, LambdaOutputParser, CaseInsensitiveDict
from samcli.lib.utils.stream_writer import StreamWriter
from samcli.local.lambdafn.exceptions import FunctionNotFound
from samcli.local.events.api_event import ContextIdentity, RequestContext, ApiGatewayLambdaEvent
from .service_error_responses import ServiceErrorResponses
Expand Down Expand Up @@ -40,16 +41,22 @@ def __init__(self, routing_list, lambda_runner, static_dir=None, port=None, host
"""
Creates an ApiGatewayService
:param list(ApiGatewayCallModel) routing_list: A list of the Model that represent
the service paths to create.
:param samcli.commands.local.lib.local_lambda.LocalLambdaRunner lambda_runner: The Lambda runner class capable
of invoking the function
:param str static_dir: Directory from which to serve static files
:param int port: Optional. port for the service to start listening on
Defaults to 3000
:param str host: Optional. host to start the service on
Defaults to '127.0.0.1
:param io.BaseIO stderr: Optional stream where the stderr from Docker container should be written to
Parameters
----------
routing_list list(ApiGatewayCallModel)
A list of the Model that represent the service paths to create.
lambda_runner samcli.commands.local.lib.local_lambda.LocalLambdaRunner
The Lambda runner class capable of invoking the function
static_dir str
Directory from which to serve static files
port int
Optional. port for the service to start listening on
Defaults to 3000
host str
Optional. host to start the service on
Defaults to '127.0.0.1
stderr samcli.lib.utils.stream_writer.StreamWriter
Optional stream writer where the stderr from Docker container should be written to
"""
super(LocalApigwService, self).__init__(lambda_runner.is_debugging(), port=port, host=host)
self.routing_list = routing_list
Expand Down Expand Up @@ -123,9 +130,14 @@ def _request_handler(self, **kwargs):
* We then transform the response or errors we get from the Invoke and return the data back to
the caller
:param kwargs dict: Keyword Args that are passed to the function from Flask. This happens when we have
Path Parameters.
:return: Response object
Parameters
----------
kwargs dict
Keyword Args that are passed to the function from Flask. This happens when we have path parameters
Returns
-------
Response object
"""
route = self._get_current_route(request)

Expand All @@ -135,9 +147,10 @@ def _request_handler(self, **kwargs):
return ServiceErrorResponses.lambda_failure_response()

stdout_stream = io.BytesIO()
stdout_stream_writer = StreamWriter(stdout_stream, self.is_debugging)

try:
self.lambda_runner.invoke(route.function_name, event, stdout=stdout_stream, stderr=self.stderr)
self.lambda_runner.invoke(route.function_name, event, stdout=stdout_stream_writer, stderr=self.stderr)
except FunctionNotFound:
return ServiceErrorResponses.lambda_not_found_response()

Expand Down
Loading

0 comments on commit afce048

Please sign in to comment.