Repository: getmoto/moto (python)

Issue:
lambci/lambda docker images are deprecated
lambci/lambda docker images are deprecated and only support nodejs images until version 12. https://github.com/lambci/docker-lambda

Those base images should be replaced with aws lambda base image. https://github.com/aws/aws-lambda-base-images

Pull request:
AWSLambda: Try to pull images from multiple repositories
Closes #5886 

When invoking an AWSLambda function, the requested image can be found in one of these repos:
 - `mlupin/docker-lambda` (the repo with up-to-date AWSLambda images
 - `lambci/lambda` (the repo with older/outdated AWSLambda images
 
This PR ensures that we cycle through all of them - when we find the repo that contains our image, we use that one.

For most users, this means the `MOTO_DOCKER_LAMBDA_IMAGE`-variable is no longer required. But we'll keep the functionality in case people want to configure a different repository instead.

Patch:
diff --git a/moto/awslambda/models.py b/moto/awslambda/models.py
index b96d3ba7a..a28096568 100644
--- a/moto/awslambda/models.py
+++ b/moto/awslambda/models.py
@@ -765,9 +765,27 @@ class LambdaFunction(CloudFormationModel, DockerModel):
                             "host.docker.internal": "host-gateway"
                         }
 
-                    image_repo = settings.moto_lambda_image()
-                    image_ref = f"{image_repo}:{self.run_time}"
-                    self.docker_client.images.pull(":".join(parse_image_ref(image_ref)))
+                    # The requested image can be found in one of a few repos:
+                    # - User-provided repo
+                    # - mlupin/docker-lambda (the repo with up-to-date AWSLambda images
+                    # - lambci/lambda (the repo with older/outdated AWSLambda images
+                    #
+                    # We'll cycle through all of them - when we find the repo that contains our image, we use it
+                    image_repos = set(
+                        [
+                            settings.moto_lambda_image(),
+                            "mlupin/docker-lambda",
+                            "lambci/lambda",
+                        ]
+                    )
+                    for image_repo in image_repos:
+                        image_ref = f"{image_repo}:{self.run_time}"
+                        full_ref = ":".join(parse_image_ref(image_ref))
+                        try:
+                            self.docker_client.images.pull(full_ref)
+                            break
+                        except docker.errors.NotFound:
+                            pass
                     container = self.docker_client.containers.run(
                         image_ref,
                         [self.handler, json.dumps(event)],
@@ -1483,13 +1501,18 @@ class LambdaBackend(BaseBackend):
         # Note that this option will be ignored if MOTO_DOCKER_NETWORK_NAME is also set
         MOTO_DOCKER_NETWORK_MODE=host moto_server
 
-    The Docker images used by Moto are taken from the `lambci/lambda`-repo by default. Use the following environment variable to configure a different repo:
+    The Docker images used by Moto are taken from the following repositories:
+
+    - `mlupin/docker-lambda` (for recent versions)
+    - `lambci/lambda` (for older/outdated versions)
+
+    Use the following environment variable to configure Moto to look for images in an additional repository:
 
     .. sourcecode:: bash
 
-        MOTO_DOCKER_LAMBDA_IMAGE=mLupin/docker-lambda
+        MOTO_DOCKER_LAMBDA_IMAGE=mlupin/docker-lambda
 
-    .. note:: When using the decorators, a Docker container cannot reach Moto, as it does not run as a server. Any boto3-invocations used within your Lambda will try to connect to AWS.
+    .. note:: When using the decorators, a Docker container cannot reach Moto, as the Docker-container loses all mock-context. Any boto3-invocations used within your Lambda will try to connect to AWS.
     """
 
     def __init__(self, region_name: str, account_id: str):
diff --git a/moto/glue/models.py b/moto/glue/models.py
index 26cfbbc0e..6b27c85df 100644
--- a/moto/glue/models.py
+++ b/moto/glue/models.py
@@ -738,6 +738,13 @@ class GlueBackend(BaseBackend):
                 )
         return errors_output
 
+    def batch_get_crawlers(self, crawler_names):
+        crawlers = []
+        for crawler in self.get_crawlers():
+            if crawler.as_dict()["Name"] in crawler_names:
+                crawlers.append(crawler.as_dict())
+        return crawlers
+
 
 class FakeDatabase(BaseModel):
     def __init__(self, database_name, database_input):
diff --git a/moto/glue/responses.py b/moto/glue/responses.py
index 486e0bee7..361848a3b 100644
--- a/moto/glue/responses.py
+++ b/moto/glue/responses.py
@@ -498,3 +498,16 @@ class GlueResponse(BaseResponse):
         description = self._get_param("Description")
         schema = self.glue_backend.update_schema(schema_id, compatibility, description)
         return json.dumps(schema)
+
+    def batch_get_crawlers(self):
+        crawler_names = self._get_param("CrawlerNames")
+        crawlers = self.glue_backend.batch_get_crawlers(crawler_names)
+        crawlers_not_found = list(
+            set(crawler_names) - set(map(lambda crawler: crawler["Name"], crawlers))
+        )
+        return json.dumps(
+            {
+                "Crawlers": crawlers,
+                "CrawlersNotFound": crawlers_not_found,
+            }
+        )
diff --git a/moto/server.py b/moto/server.py
index 8d1570505..b451943a1 100644
--- a/moto/server.py
+++ b/moto/server.py
@@ -2,6 +2,8 @@ import argparse
 import os
 import signal
 import sys
+import warnings
+
 from werkzeug.serving import run_simple
 
 from moto.moto_server.werkzeug_app import (
@@ -70,6 +72,11 @@ def main(argv=None):
     except Exception:
         pass  # ignore "ValueError: signal only works in main thread"
 
+    if args.service:
+        warnings.warn(
+            f"The service-argument ({args.service}) is considered deprecated, and will be deprecated in a future release. Please let us know if you have any questions: https://github.com/getmoto/moto/issues"
+        )
+
     # Wrap the main application
     main_app = DomainDispatcherApplication(create_backend_app, service=args.service)
     main_app.debug = True
diff --git a/moto/settings.py b/moto/settings.py
index 8f5ce123d..652b74163 100644
--- a/moto/settings.py
+++ b/moto/settings.py
@@ -89,7 +89,7 @@ def moto_server_host() -> str:
 
 
 def moto_lambda_image() -> str:
-    return os.environ.get("MOTO_DOCKER_LAMBDA_IMAGE", "lambci/lambda")
+    return os.environ.get("MOTO_DOCKER_LAMBDA_IMAGE", "mlupin/docker-lambda")
 
 
 def moto_network_name() -> str:


Test patch:
diff --git a/tests/test_awslambda/test_lambda_invoke.py b/tests/test_awslambda/test_lambda_invoke.py
index b7294ec7b..7741f679a 100644
--- a/tests/test_awslambda/test_lambda_invoke.py
+++ b/tests/test_awslambda/test_lambda_invoke.py
@@ -123,7 +123,7 @@ def test_invoke_event_function():
     function_name = str(uuid4())[0:6]
     conn.create_function(
         FunctionName=function_name,
-        Runtime="python2.7",
+        Runtime="python3.9",
         Role=get_role_name(),
         Handler="lambda_function.lambda_handler",
         Code={"ZipFile": get_test_zip_file1()},
diff --git a/tests/test_glue/test_glue.py b/tests/test_glue/test_glue.py
index 5e3fcbfce..507b7affc 100644
--- a/tests/test_glue/test_glue.py
+++ b/tests/test_glue/test_glue.py
@@ -401,3 +401,16 @@ def test_untag_glue_crawler():
     resp = client.get_tags(ResourceArn=resource_arn)
 
     resp.should.have.key("Tags").equals({"key1": "value1", "key3": "value3"})
+
+
+@mock_glue
+def test_batch_get_crawlers():
+    client = create_glue_client()
+    crawler_name = create_test_crawler(client)
+
+    response = client.batch_get_crawlers(
+        CrawlerNames=[crawler_name, "crawler-not-found"]
+    )
+
+    response["Crawlers"].should.have.length_of(1)
+    response["CrawlersNotFound"].should.have.length_of(1)