Lambda: add function properties to LambdaContext for invocations
diff --git a/localstack/services/awslambda/lambda_api.py b/localstack/services/awslambda/lambda_api.py
index c5156db076fde..c129b101430e9 100644
--- a/localstack/services/awslambda/lambda_api.py
+++ b/localstack/services/awslambda/lambda_api.py
@@ -10,6 +10,7 @@
 import base64
 import threading
 import imp
+import re
 from io import BytesIO
 from datetime import datetime
 from six import iteritems
@@ -72,6 +73,15 @@
 
 
 class LambdaContext(object):
+
+    def __init__(self, func_details, qualifier=None):
+        self.function_name = func_details.name()
+        self.function_version = func_details.get_qualifier_version(qualifier)
+
+        self.invoked_function_arn = func_details.arn()
+        if qualifier:
+            self.invoked_function_arn += ':' + qualifier
+
     def get_remaining_time_in_millis(self):
         # TODO implement!
         return 1000 * 60
@@ -254,7 +264,7 @@ def run_lambda(event, context, func_arn, version=None, suppress_output=False, as
     try:
         func_details = arn_to_lambda.get(func_arn)
         if not context:
-            context = LambdaContext()
+            context = LambdaContext(func_details, version)
         result, log_output = LAMBDA_EXECUTOR.execute(func_arn, func_details,
             event, context=context, version=version, async=async)
     except Exception as e:
@@ -531,6 +541,8 @@ def create_function():
             'TracingConfig': {},
             'VpcConfig': {'SecurityGroupIds': [None], 'SubnetIds': [None], 'VpcId': None}
         })
+        if data.get('Publish', False):
+            result['Version'] = publish_new_function_version(arn)['Version']
         return jsonify(result or {})
     except Exception as e:
         del arn_to_lambda[arn]
@@ -693,11 +705,20 @@ def invoke_function(function):
             - name: 'request'
               in: body
     """
+    # function here can either be an arn or a function name
     arn = func_arn(function)
+
+    # arn can also contain a qualifier, extract it from there if so
+    m = re.match('(arn:aws:lambda:.*:.*:function:[a-zA-Z0-9-_]+)(:.*)?', arn)
+    if m and m.group(2):
+        qualifier = m.group(2)[1:]
+        arn = m.group(1)
+    else:
+        qualifier = request.args.get('Qualifier')
+
     if arn not in arn_to_lambda:
         return error_response('Function does not exist: %s' % arn, 404, error_type='ResourceNotFoundException')
-    qualifier = request.args['Qualifier'] if 'Qualifier' in request.args else '$LATEST'
-    if not arn_to_lambda.get(arn).qualifier_exists(qualifier):
+    if qualifier and not arn_to_lambda.get(arn).qualifier_exists(qualifier):
         return error_response('Function does not exist: {0}:{1}'.format(arn, qualifier), 404,
                               error_type='ResourceNotFoundException')
     data = None
diff --git a/localstack/services/awslambda/lambda_executors.py b/localstack/services/awslambda/lambda_executors.py
index 9e2015f8d03ef..d319327b1167d 100644
--- a/localstack/services/awslambda/lambda_executors.py
+++ b/localstack/services/awslambda/lambda_executors.py
@@ -110,6 +110,10 @@ def execute(self, func_arn, func_details, event, context=None, version=None, asy
         environment['AWS_LAMBDA_EVENT_BODY'] = event_body_escaped
         environment['HOSTNAME'] = docker_host
         environment['LOCALSTACK_HOSTNAME'] = docker_host
+        if context:
+            environment['AWS_LAMBDA_FUNCTION_NAME'] = context.function_name
+            environment['AWS_LAMBDA_FUNCTION_VERSION'] = context.function_version
+            environment['AWS_LAMBDA_FUNCTION_INVOKED_ARN'] = context.invoked_function_arn
 
         # custom command to execute in the container
         command = ''
diff --git a/localstack/utils/aws/aws_models.py b/localstack/utils/aws/aws_models.py
index dc59fef5daa24..d4b6a1b852e30 100644
--- a/localstack/utils/aws/aws_models.py
+++ b/localstack/utils/aws/aws_models.py
@@ -181,11 +181,13 @@ def arn(self):
         return self.id
 
     def function(self, qualifier=None):
+        return self.versions.get(self.get_qualifier_version(qualifier)).get('Function')
+
+    def get_qualifier_version(self, qualifier=None):
         if not qualifier:
             qualifier = '$LATEST'
-        version = qualifier if qualifier in self.versions else \
+        return qualifier if qualifier in self.versions else \
             self.aliases.get(qualifier).get('FunctionVersion')
-        return self.versions.get(version).get('Function')
 
     def qualifier_exists(self, qualifier):
         return qualifier in self.aliases or qualifier in self.versions
diff --git a/tests/integration/lambdas/lambda_integration.py b/tests/integration/lambdas/lambda_integration.py
index 363ffd0d74c6e..3d84a5124e209 100644
--- a/tests/integration/lambdas/lambda_integration.py
+++ b/tests/integration/lambdas/lambda_integration.py
@@ -35,7 +35,14 @@ def handler(event, context):
         }
 
     if 'Records' not in event:
-        return event
+        return {
+            'event': event,
+            'context': {
+                'invoked_function_arn': context.invoked_function_arn,
+                'function_version': context.function_version,
+                'function_name': context.function_name
+            }
+        }
 
     raw_event_messages = []
     for record in event['Records']:
diff --git a/tests/integration/test_lambda.py b/tests/integration/test_lambda.py
index 4fbe0848c25ed..01346db42b6c5 100644
--- a/tests/integration/test_lambda.py
+++ b/tests/integration/test_lambda.py
@@ -61,8 +61,52 @@ def test_upload_lambda_from_s3():
     # invoke lambda function
     data_before = b'{"foo": "bar"}'
     result = lambda_client.invoke(FunctionName=lambda_name, Payload=data_before)
-    data_after = result['Payload'].read()
-    assert json.loads(to_str(data_before)) == json.loads(to_str(data_after))
+    data_after = json.loads(result['Payload'].read())
+    assert json.loads(to_str(data_before)) == data_after['event']
+
+    context = data_after['context']
+    assert '$LATEST' == context['function_version']
+    assert lambda_name == context['function_name']
+
+
+def test_function_invocation_with_qualifier():
+
+    s3_client = aws_stack.connect_to_service('s3')
+    lambda_client = aws_stack.connect_to_service('lambda')
+
+    lambda_name = 'test_lambda_%s' % short_uid()
+    bucket_name = 'test_bucket_lambda2'
+    bucket_key = 'test_lambda.zip'
+
+    # upload zip file to S3
+    zip_file = testutil.create_lambda_archive(load_file(TEST_LAMBDA_PYTHON), get_content=True,
+        libs=TEST_LAMBDA_LIBS, runtime=LAMBDA_RUNTIME_PYTHON27)
+    s3_client.create_bucket(Bucket=bucket_name)
+    s3_client.upload_fileobj(BytesIO(zip_file), bucket_name, bucket_key)
+
+    # create lambda function
+    response = lambda_client.create_function(
+        FunctionName=lambda_name, Handler='handler.handler',
+        Runtime=lambda_api.LAMBDA_RUNTIME_PYTHON27, Role='r1',
+        Code={
+            'S3Bucket': bucket_name,
+            'S3Key': bucket_key
+        },
+        Publish=True
+    )
+
+    assert 'Version' in response
+
+    # invoke lambda function
+    data_before = b'{"foo": "bar"}'
+    result = lambda_client.invoke(FunctionName=lambda_name, Payload=data_before,
+                                  Qualifier=response['Version'])
+    data_after = json.loads(result['Payload'].read())
+    assert json.loads(to_str(data_before)) == data_after['event']
+
+    context = data_after['context']
+    assert response['Version'] == context['function_version']
+    assert lambda_name == context['function_name']
 
 
 def test_lambda_runtimes():
@@ -76,8 +120,8 @@ def test_lambda_runtimes():
         zip_file=zip_file, runtime=LAMBDA_RUNTIME_PYTHON27)
     result = lambda_client.invoke(FunctionName=TEST_LAMBDA_NAME_PY, Payload=b'{}')
     assert result['StatusCode'] == 200
-    result_data = result['Payload'].read()
-    assert to_str(result_data).strip() == '{}'
+    result_data = json.loads(result['Payload'].read())
+    assert result_data['event'] == json.loads('{}')
 
     if use_docker():
         # deploy and invoke lambda - Python 3.6

This adds some common properties to the LambdaContext, which is passed
to functions during invocations. In particular, the following properties
are added: function_name, function_version, invoked_function_arn.

The above properties are passed to lambda functions running inside docker.

Support for publishing during function creation has also been added,
which was a missing functionality, and was helpful in testing.

Note that this has a slight dependency on https://github.com/lambci/docker-lambda/pull/98, but shouldn't break anything if this was merged first.

Contributed by @quantcast

**Please refer to the contribution guidelines in the README when submitting PRs.**
