Repository: getmoto/moto

Issue:
missing features from awslambda mock
In the process of writing tests for some lambda function management code, I've noticed that a number of features are not implemented. Interestingly, there are no exceptions raised by `moto`. Instead, the calls are pushed through to AWS, and time out due to insufficient permissions (thank goodness!). These calls include:

* awslambda.add_permission
* awslambda.remove_permission
* awslambda.update_function_code
* awslambda.update_function_configuration

These were problematic enough for my testing that I didn't go any deeper. A quick look at the code suggests that there are other functions that would be missing as well.

Rounding out the implementation for awslambda is one thing, but the fact that some calls are caught and others are passed through to lambda proper is another. If a user has default credentials set up, they may end up making costly calls to the real service while developing tests. Any chance of a quick solution to that secondary problem?


Discussion:
On a related note, despite the fact that it appears to be implemented in `moto`, I'm getting the same behavior from `awslambda.delete_function`.

It occurs to me that it's possible that I misinterpreted the error initially. I'm not totally sure that this is a pass-through to lambda-proper. Here's the error:

```python
ConnectionClosedError: Connection was closed before we received a valid response from endpoint URL: "https://lambda.us-east-1.amazonaws.com/2015-03-31/functions/arn%3Aaws%3Alambda%3A123456789012%3Afunction%3Abar".
```

Is there something within `moto` that could be catching that but failing to return?  Here's some potentially helpful log output:

```
INFO:botocore.vendored.requests.packages.urllib3.connectionpool:Starting new HTTP connection (1): 169.254.169.254
INFO:botocore.vendored.requests.packages.urllib3.connectionpool:Starting new HTTP connection (1): 169.254.169.254
INFO:boto3.resources.action:Calling iam:create_role with {'RoleName': 'baz-foo-role', 'AssumeRolePolicyDocument': '"some": "policy"'}
INFO:botocore.vendored.requests.packages.urllib3.connectionpool:Starting new HTTPS connection (1): iam.amazonaws.com
INFO:mylogger:creating lambda function: bar
INFO:boto3.resources.action:Calling iam:get_role with {u'RoleName': 'baz-foo-role'}
INFO:botocore.vendored.requests.packages.urllib3.connectionpool:Starting new HTTPS connection (1): iam.amazonaws.com
INFO:botocore.vendored.requests.packages.urllib3.connectionpool:Starting new HTTPS connection (1): lambda.us-east-1.amazonaws.com
INFO:mylogger:successfully created lambda function: arn:aws:lambda:123456789012:function:bar
INFO:mylogger:deleting lambda function: arn:aws:lambda:123456789012:function:bar
INFO:botocore.vendored.requests.packages.urllib3.connectionpool:Starting new HTTPS connection (1): lambda.us-east-1.amazonaws.com
INFO:botocore.vendored.requests.packages.urllib3.connectionpool:Starting new HTTPS connection (2): lambda.us-east-1.amazonaws.com
INFO:botocore.vendored.requests.packages.urllib3.connectionpool:Starting new HTTPS connection (3): lambda.us-east-1.amazonaws.com
INFO:botocore.vendored.requests.packages.urllib3.connectionpool:Starting new HTTPS connection (4): lambda.us-east-1.amazonaws.com
INFO:botocore.vendored.requests.packages.urllib3.connectionpool:Starting new HTTPS connection (5): lambda.us-east-1.amazonaws.com
```
Yes, that is not actually going to AWS, but being stopped by Responses.

Keeping this ticket open for the feature requests. Thanks!

Resolving patch:
diff --git a/moto/awslambda/models.py b/moto/awslambda/models.py
--- a/moto/awslambda/models.py
+++ b/moto/awslambda/models.py
@@ -1006,11 +1006,11 @@ def untag_resource(self, resource, tagKeys):
             return True
         return False
 
-    def add_policy_statement(self, function_name, raw):
+    def add_permission(self, function_name, raw):
         fn = self.get_function(function_name)
         fn.policy.add_statement(raw)
 
-    def del_policy_statement(self, function_name, sid, revision=""):
+    def remove_permission(self, function_name, sid, revision=""):
         fn = self.get_function(function_name)
         fn.policy.del_statement(sid, revision)
 
diff --git a/moto/awslambda/responses.py b/moto/awslambda/responses.py
--- a/moto/awslambda/responses.py
+++ b/moto/awslambda/responses.py
@@ -146,7 +146,7 @@ def _add_policy(self, request, full_url, headers):
         function_name = path.split("/")[-2]
         if self.lambda_backend.get_function(function_name):
             statement = self.body
-            self.lambda_backend.add_policy_statement(function_name, statement)
+            self.lambda_backend.add_permission(function_name, statement)
             return 200, {}, json.dumps({"Statement": statement})
         else:
             return 404, {}, "{}"
@@ -166,9 +166,7 @@ def _del_policy(self, request, full_url, headers, querystring):
         statement_id = path.split("/")[-1].split("?")[0]
         revision = querystring.get("RevisionId", "")
         if self.lambda_backend.get_function(function_name):
-            self.lambda_backend.del_policy_statement(
-                function_name, statement_id, revision
-            )
+            self.lambda_backend.remove_permission(function_name, statement_id, revision)
             return 204, {}, "{}"
         else:
             return 404, {}, "{}"


Test patch:
diff --git a/tests/test_awslambda/test_lambda.py b/tests/test_awslambda/test_lambda.py
--- a/tests/test_awslambda/test_lambda.py
+++ b/tests/test_awslambda/test_lambda.py
@@ -1677,6 +1677,42 @@ def test_create_function_with_unknown_arn():
     )
 
 
+@mock_lambda
+def test_remove_function_permission():
+    conn = boto3.client("lambda", _lambda_region)
+    zip_content = get_test_zip_file1()
+    conn.create_function(
+        FunctionName="testFunction",
+        Runtime="python2.7",
+        Role=(get_role_name()),
+        Handler="lambda_function.handler",
+        Code={"ZipFile": zip_content},
+        Description="test lambda function",
+        Timeout=3,
+        MemorySize=128,
+        Publish=True,
+    )
+
+    conn.add_permission(
+        FunctionName="testFunction",
+        StatementId="1",
+        Action="lambda:InvokeFunction",
+        Principal="432143214321",
+        SourceArn="arn:aws:lambda:us-west-2:account-id:function:helloworld",
+        SourceAccount="123412341234",
+        EventSourceToken="blah",
+        Qualifier="2",
+    )
+
+    remove = conn.remove_permission(
+        FunctionName="testFunction", StatementId="1", Qualifier="2",
+    )
+    remove["ResponseMetadata"]["HTTPStatusCode"].should.equal(204)
+    policy = conn.get_policy(FunctionName="testFunction", Qualifier="2")["Policy"]
+    policy = json.loads(policy)
+    policy["Statement"].should.equal([])
+
+
 def create_invalid_lambda(role):
     conn = boto3.client("lambda", _lambda_region)
     zip_content = get_test_zip_file1()