Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -378,5 +378,6 @@ $RECYCLE.BIN/
# Windows shortcuts
*.lnk

/Dockerfile

# End of https://www.gitignore.io/api/osx,node,macos,linux,python,windows,pycharm,intellij,sublimetext,visualstudiocode
# End of https://www.gitignore.io/api/osx,node,macos,linux,python,windows,pycharm,intellij,sublimetext,visualstudiocode
6 changes: 6 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
include LICENSE
include requirements/base.txt
include requirements/dev.txt
recursive-include aws_lambda_builders/workflows *
prune tests

4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ init:

test:
# Run unit tests
# Fail if coverage falls below 95%
LAMBDA_BUILDERS_DEV=1 pytest --cov aws_lambda_builders --cov-report term-missing --cov-fail-under 95 tests/unit tests/functional
# Fail if coverage falls below 90%
LAMBDA_BUILDERS_DEV=1 pytest --cov aws_lambda_builders --cov-report term-missing --cov-fail-under 90 tests/unit tests/functional

func-test:
LAMBDA_BUILDERS_DEV=1 pytest tests/functional
Expand Down
69 changes: 56 additions & 13 deletions aws_lambda_builders/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,76 @@

import sys
import json
import os
import logging

from aws_lambda_builders.builder import LambdaBuilder
from aws_lambda_builders.exceptions import WorkflowNotFoundError, WorkflowUnknownError, WorkflowFailedError


def _success_response(request_id):
log_level = int(os.environ.get("LAMBDA_BUILDERS_LOG_LEVEL", logging.INFO))

# Write output to stderr because stdout is used for command response
logging.basicConfig(stream=sys.stderr,
level=log_level,
format='[Lambda Builders] %(asctime)s - %(levelname)s - %(message)s')

LOG = logging.getLogger(__name__)


def _success_response(request_id, artifacts_dir):
return json.dumps({
"jsonrpc": "2.0",
"id": request_id,
"result": {}
"result": {
"artifacts_dir": artifacts_dir
}
})


def main():
def _error_response(request_id, http_status_code, message):
return json.dumps({
"jsonrpc": "2.0",
"id": request_id,
"error": {
"code": http_status_code,
"message": message
}
})


def main(): # pylint: disable=too-many-statements
"""
Implementation of CLI Interface. Handles only one JSON-RPC method at a time and responds with data

Input is passed as JSON string either through stdin or as the first argument to the command. Output is always
printed to stdout.
"""

# For now the request is not validated
request = json.load(sys.stdin)
if len(sys.argv) > 1:
request_str = sys.argv[1]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does that mean its anything passed as an argument right now?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol yes. I am trying to incrementally evolve the CLI interface. I know this is bad, but before release, we will get a more stabler CLI interface

LOG.debug("Using the request object from command line argument")
else:
LOG.debug("Reading the request object from stdin")
request_str = sys.stdin.read()

request = json.loads(request_str)

request_id = request["id"]
params = request["params"]
capabilities = params["capability"]
supported_workflows = params["supported_workflows"]
supported_workflows = params.get("supported_workflows")

exit_code = 0
response = None
try:
builder = LambdaBuilder(language=capabilities["language"],
dependency_manager=capabilities["dependency_manager"],
application_framework=capabilities["application_framework"],
supported_workflows=supported_workflows)

artifacts_dir = params["artifacts_dir"]
builder.build(params["source_dir"],
params["artifacts_dir"],
params["scratch_dir"],
Expand All @@ -48,17 +87,21 @@ def main():
options=params["options"])

# Return a success response
sys.stdout.write(_success_response(request_id))
sys.stdout.flush() # Make sure it is written
response = _success_response(request_id, artifacts_dir)

except (WorkflowNotFoundError, WorkflowUnknownError, WorkflowFailedError) as ex:
# TODO: Return a workflow error response
print(str(ex))
sys.exit(1)
LOG.debug("Builder workflow failed", exc_info=ex)
exit_code = 1
response = _error_response(request_id, 400, str(ex))

except Exception as ex:
# TODO: Return a internal server response
print(str(ex))
sys.exit(1)
LOG.debug("Builder crashed", exc_info=ex)
exit_code = 1
response = _error_response(request_id, 500, str(ex))

sys.stdout.write(response)
sys.stdout.flush() # Make sure it is written
sys.exit(exit_code)


if __name__ == '__main__':
Expand Down
1 change: 1 addition & 0 deletions aws_lambda_builders/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def copytree(source, destination, ignore=None):

new_source = os.path.join(source, name)
new_destination = os.path.join(destination, name)

if os.path.isdir(new_source):
copytree(new_source, new_destination, ignore=ignore)
else:
Expand Down
1 change: 1 addition & 0 deletions requirements/base.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
six~=1.11.0
30 changes: 24 additions & 6 deletions tests/functional/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import copy

from unittest import TestCase
from parameterized import parameterized


class TestCliWithHelloWorkflow(TestCase):
Expand All @@ -29,15 +30,20 @@ def setUp(self):
self.expected_contents = "Hello World"

self.command_name = "lambda-builders-dev" if os.environ.get("LAMBDA_BUILDERS_DEV") else "lambda-builders"

# Make sure the test workflow is in PYTHONPATH to be automatically loaded
self.python_path_list = os.environ.get("PYTHONPATH", '').split(os.pathsep) + [self.TEST_WORKFLOWS_FOLDER]
self.python_path = os.pathsep.join(filter(bool, self.python_path_list))
print(self.python_path)

def tearDown(self):
shutil.rmtree(self.source_dir)
shutil.rmtree(self.artifacts_dir)

def test_run_hello_workflow(self):
@parameterized.expand([
("request_through_stdin"),
("request_through_argument")
])
def test_run_hello_workflow(self, flavor):

request_json = json.dumps({
"jsonschema": "2.0",
Expand All @@ -57,14 +63,26 @@ def test_run_hello_workflow(self):
"optimizations": {},
"options": {},
}
}).encode('utf-8')
})

env = copy.deepcopy(os.environ)
env["PYTHONPATH"] = self.python_path

p = subprocess.Popen([self.command_name], env=env, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
stdout_data = p.communicate(input=request_json)[0]
print(stdout_data)
stdout_data = None
if flavor == "request_through_stdin":
p = subprocess.Popen([self.command_name], env=env, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
stdout_data = p.communicate(input=request_json.encode('utf-8'))[0]
elif flavor == "request_through_argument":
p = subprocess.Popen([self.command_name, request_json], env=env, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
stdout_data = p.communicate()[0]
else:
raise ValueError("Invalid test flavor")

# Validate the response object. It should be successful response
response = json.loads(stdout_data)
self.assertNotIn('error', response)
self.assertIn('result', response)
self.assertEquals(response['result']['artifacts_dir'], self.artifacts_dir)

self.assertTrue(os.path.exists(self.expected_filename))
contents = ''
Expand Down
1 change: 0 additions & 1 deletion tests/unit/workflows/python_pip/test_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,3 @@ def test_must_raise_exception_on_failure(self, PythonPipDependencyBuilderMock):

with self.assertRaises(ActionFailedError):
action.execute()