Skip to content

Commit

Permalink
Merge pull request #752 from awslabs/develop
Browse files Browse the repository at this point in the history
Release: 0.6.2 Release
  • Loading branch information
sriram-mv committed Nov 7, 2018
2 parents aa611aa + 480979f commit 5febb4e
Show file tree
Hide file tree
Showing 16 changed files with 130 additions and 18 deletions.
12 changes: 9 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
# Enable container based builds
sudo: false
sudo: required
language: python

services:
- docker

python:
- "2.7"
- "3.6"

env:
global:
- AWS_DEFAULT_REGION=us-east-1

# Enable 3.7 without globally enabling sudo and dist: xenial for other build jobs
matrix:
include:
Expand All @@ -17,8 +24,7 @@ install:
# Install the code requirements
- make init

# Install Docs requirements

script:
# Runs unit tests
- make pr
- SAM_CLI_DEV=1 travis_wait pytest -vv tests/integration
26 changes: 26 additions & 0 deletions DEVELOPMENT_GUIDE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,32 @@ if any.
#. Install dev CLI: ``make init``
#. Make sure installation succeeded: ``which samdev``

4. (Optional) Install development version of SAM Transformer
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If you want to run the latest version of [SAM Transformer](https://github.com/awslabs/serverless-application-model/), you can clone it locally and install it in your pyenv. This is useful if you want to validate your templates against any new, unreleased SAM features ahead of time.

This step is optional and will use the specified version of aws-sam-transformer from PyPi by default.

```bash
# cd into the directory where you usually place projects and clone the latest SAM Transformer
cd ~/projects
git clone https://github.com/awslabs/serverless-application-model/
# cd into the new directory and checkout the relevant branch
cd serverless-application-model
git checkout develop
# Install the SAM Transformer in editable mode so that all changes you make to
# the SAM Transformer locally are immediately picked up for SAM CLI.
pip install -e .
# Move back to your SAM CLI directory and re-run init
# If necessary: open requirements/base.txt and replace the version number of aws-sam-translator with the
# version number specified in your local version of serverless-application-model/samtranslator/__init__.py
cd ../aws-sam-cli
make init
```

Running Tests
-------------
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Learn how to get started using the SAM CLI with these guides:
- `Running and debugging serverless applications locally <docs/usage.rst>`__: Describes how to use SAM CLI for invoking Lambda functions locally, running automated tests, fetching logs, and debugging applications
- `Packaging and deploying your application <docs/deploying_serverless_applications.rst>`__: Deploy your local application using an S3 bucket, and AWS CloudFormation.
- `Advanced <docs/advanced_usage.rst>`__: Learn how to work with compiled languages (such as Java and .NET), configure IAM credentials, provide environment variables, and more.
- `Examples <#examples>`__
- `Examples <https://github.com/awslabs/serverless-application-model/tree/master/examples/apps>`__


Project Status
Expand Down
2 changes: 1 addition & 1 deletion docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ environment variable to contact the docker daemon.
pip install --upgrade setuptools
pip install --upgrade aws-sam-cli
*** if you want to use the lambda-local option(without running it as root) you will need to add your user to the docker group ***
usermod -a -G Docker yourUserName
usermod -a -G docker yourUserName
**Note for macOS and Windows users**: SAM CLI requires that the project directory
(or any parent directory) is listed in `Docker file sharing options <https://docs.docker.com/docker-for-mac/osxfs/>`__.
Expand Down
2 changes: 1 addition & 1 deletion requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Flask~=1.0.2
boto3~=1.5
PyYAML~=3.12
cookiecutter~=1.6.0
aws-sam-translator==1.7.0
aws-sam-translator==1.8.0
docker>=3.3.0
dateparser~=0.7
python-dateutil~=2.6
Expand Down
2 changes: 1 addition & 1 deletion requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pylint==1.7.2
pytest==3.0.7
py==1.4.33
mock==2.0.0
requests==2.19.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.6.1'
__version__ = '0.6.2'
2 changes: 1 addition & 1 deletion samcli/local/apigw/local_apigw_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ def _query_string_params(flask_request):

# Flask returns an ImmutableMultiDict so convert to a dictionary that becomes
# a dict(str: list) then iterate over
for query_string_key, query_string_list in dict(flask_request.args).items():
for query_string_key, query_string_list in flask_request.args.lists():
query_string_value_length = len(query_string_list)

# if the list is empty, default to empty string
Expand Down
5 changes: 4 additions & 1 deletion samcli/local/docker/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,13 @@ def create(self):
# Ex: 128m => 128MB
kwargs["mem_limit"] = "{}m".format(self._memory_limit_mb)

if self.network_id == 'host':
kwargs["network_mode"] = self.network_id

real_container = self.docker_client.containers.create(self._image, **kwargs)
self.id = real_container.id

if self.network_id:
if self.network_id and self.network_id != 'host':
network = self.docker_client.networks.get(self.network_id)
network.connect(self.id)

Expand Down
2 changes: 1 addition & 1 deletion samcli/local/init/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ def __init__(self, **kwargs):

class GenerateProjectFailedError(InitErrorException):
fmt = \
("An error ocurred while generating this {project}: {provider_error}")
("An error occurred while generating this {project}: {provider_error}")
Original file line number Diff line number Diff line change
@@ -1 +1 @@
requests==2.18.4
requests==2.20.0
2 changes: 1 addition & 1 deletion tests/functional/local/apigw/test_local_apigw_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,7 @@ def make_service(list_of_routes, function_provider, cwd):

def make_service_response(port, method, scheme, resourcePath, resolvedResourcePath, pathParameters=None,
body=None, headers=None, queryParams=None, isBase64Encoded=False):
response_str = '{"httpMethod": "GET", "body": null, "resource": "/something/{event}", "requestContext": {"resourceId": "123456", "apiId": "1234567890", "resourcePath": "/something/{event}", "httpMethod": "GET", "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", "accountId": "123456789012", "stage": "prod", "identity": {"apiKey": null, "userArn": null, "cognitoAuthenticationType": null, "caller": null, "userAgent": "Custom User Agent String", "user": null, "cognitoIdentityPoolId": null, "cognitoAuthenticationProvider": null, "sourceIp": "127.0.0.1", "accountId": null}, "extendedRequestId": null, "path": "/something/{event}"}, "queryStringParameters": null, "headers": {"Host": "0.0.0.0:33651", "User-Agent": "python-requests/2.19.0", "Accept-Encoding": "gzip, deflate", "Accept": "*/*", "Connection": "keep-alive"}, "pathParameters": {"event": "event1"}, "stageVariables": null, "path": "/something/event1", "isBase64Encoded": false}' # NOQA
response_str = '{"httpMethod": "GET", "body": null, "resource": "/something/{event}", "requestContext": {"resourceId": "123456", "apiId": "1234567890", "resourcePath": "/something/{event}", "httpMethod": "GET", "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", "accountId": "123456789012", "stage": "prod", "identity": {"apiKey": null, "userArn": null, "cognitoAuthenticationType": null, "caller": null, "userAgent": "Custom User Agent String", "user": null, "cognitoIdentityPoolId": null, "cognitoAuthenticationProvider": null, "sourceIp": "127.0.0.1", "accountId": null}, "extendedRequestId": null, "path": "/something/{event}"}, "queryStringParameters": null, "headers": {"Host": "0.0.0.0:33651", "User-Agent": "python-requests/2.20.0", "Accept-Encoding": "gzip, deflate", "Accept": "*/*", "Connection": "keep-alive"}, "pathParameters": {"event": "event1"}, "stageVariables": null, "path": "/something/event1", "isBase64Encoded": false}' # NOQA
response = json.loads(response_str)

if body:
Expand Down
5 changes: 4 additions & 1 deletion tests/integration/local/invoke/invoke_integ_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def base_command(cls):
return command

def get_command_list(self, function_to_invoke, template_path=None, event_path=None, env_var_path=None,
parameter_overrides=None, region=None):
parameter_overrides=None, region=None, docker_network=None):
command_list = [self.cmd, "local", "invoke", function_to_invoke]

if template_path:
Expand All @@ -41,6 +41,9 @@ def get_command_list(self, function_to_invoke, template_path=None, event_path=No
if env_var_path:
command_list = command_list + ["-n", env_var_path]

if docker_network:
command_list = command_list + ["--docker-network", docker_network]

if parameter_overrides:
arg_value = " ".join([
"ParameterKey={},ParameterValue={}".format(key, value) for key, value in parameter_overrides.items()
Expand Down
11 changes: 11 additions & 0 deletions tests/integration/local/invoke/test_integrations_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,14 @@ def test_invoke_with_env_using_parameters_with_custom_region(self):
environ = json.loads(process_stdout.decode('utf-8'))

self.assertEquals(environ["Region"], custom_region)

def test_invoke_with_docker_network_of_host(self):
command_list = self.get_command_list("HelloWorldServerlessFunction",
template_path=self.template_path,
event_path=self.event_path,
docker_network='host')

process = Popen(command_list, stdout=PIPE)
return_code = process.wait()

self.assertEquals(return_code, 0)
16 changes: 12 additions & 4 deletions tests/unit/local/apigw/test_local_apigw_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,9 @@ def setUp(self):
self.request_mock.method = "GET"
self.request_mock.remote_addr = "190.0.0.0"
self.request_mock.get_data.return_value = b"DATA!!!!"
self.request_mock.args = {"query": ["params"]}
query_param_args_mock = Mock()
query_param_args_mock.lists.return_value = {"query": ["params"]}.items()
self.request_mock.args = query_param_args_mock
self.request_mock.headers = {"Content-Type": "application/json", "X-Test": "Value"}
self.request_mock.view_args = {"path": "params"}
self.request_mock.scheme = "http"
Expand Down Expand Up @@ -433,21 +435,27 @@ def test_construct_event_with_binary_data(self, should_base64_encode_patch):

def test_query_string_params_with_empty_params(self):
request_mock = Mock()
request_mock.args = {}
query_param_args_mock = Mock()
query_param_args_mock.lists.return_value = {}.items()
request_mock.args = query_param_args_mock

actual_query_string = LocalApigwService._query_string_params(request_mock)
self.assertEquals(actual_query_string, {})

def test_query_string_params_with_param_value_being_empty_list(self):
request_mock = Mock()
request_mock.args = {"param": []}
query_param_args_mock = Mock()
query_param_args_mock.lists.return_value = {"param": []}.items()
request_mock.args = query_param_args_mock

actual_query_string = LocalApigwService._query_string_params(request_mock)
self.assertEquals(actual_query_string, {"param": ""})

def test_query_string_params_with_param_value_being_non_empty_list(self):
request_mock = Mock()
request_mock.args = {"param": ["a", "b"]}
query_param_args_mock = Mock()
query_param_args_mock.lists.return_value = {"param": ["a", "b"]}.items()
request_mock.args = query_param_args_mock

actual_query_string = LocalApigwService._query_string_params(request_mock)
self.assertEquals(actual_query_string, {"param": "b"})
Expand Down
55 changes: 55 additions & 0 deletions tests/unit/local/docker/test_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,12 @@ def test_must_connect_to_network_on_create(self):
Create a container with only required values. Optional values are not provided
:return:
"""
expected_volumes = {
self.host_dir: {
"bind": self.working_dir,
"mode": "ro"
}
}

network_id = "some id"
generated_id = "fooobar"
Expand All @@ -240,9 +246,58 @@ def test_must_connect_to_network_on_create(self):
container_id = container.create()
self.assertEquals(container_id, generated_id)

self.mock_docker_client.containers.create.assert_called_with(self.image,
command=self.cmd,
working_dir=self.working_dir,
tty=False,
volumes=expected_volumes
)

self.mock_docker_client.networks.get.assert_called_with(network_id)
network_mock.connect.assert_called_with(container_id)

def test_must_connect_to_host_network_on_create(self):
"""
Create a container with only required values. Optional values are not provided
:return:
"""
expected_volumes = {
self.host_dir: {
"bind": self.working_dir,
"mode": "ro"
}
}

network_id = "host"
generated_id = "fooobar"
self.mock_docker_client.containers.create.return_value = Mock()
self.mock_docker_client.containers.create.return_value.id = generated_id

network_mock = Mock()
self.mock_docker_client.networks.get.return_value = network_mock
network_mock.connect = Mock()

container = Container(self.image,
self.cmd,
self.working_dir,
self.host_dir,
docker_client=self.mock_docker_client)

container.network_id = network_id

container_id = container.create()
self.assertEquals(container_id, generated_id)

self.mock_docker_client.containers.create.assert_called_with(self.image,
command=self.cmd,
working_dir=self.working_dir,
tty=False,
volumes=expected_volumes,
network_mode='host'
)

self.mock_docker_client.networks.get.assert_not_called()

def test_must_fail_if_already_created(self):

container = Container(self.image,
Expand Down

0 comments on commit 5febb4e

Please sign in to comment.