When I attempt to upload a function to my Localstack instance where the runtime is specified as `provided` aka custom runtime

https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html


> To use a custom runtime, set your function's runtime to provided.
> ...
> If there's a file named `bootstrap` in your deployment package, Lambda executes that file. If not, Lambda looks for a runtime in the function's layers. If the bootstrap file isn't found or isn't executable, your function returns an error upon invocation.


So here's what's happening, when I upload the function to Localstack I get a Boto error:

```
File "/opt/code/localstack/localstack/services/cloudformation/cloudformation_starter.py", line 173, in parse_and_create_resource
localstack_1  |     return _parse_and_create_resource(logical_id, resource_json, resources_map, region_name)
localstack_1  |   File "/opt/code/localstack/localstack/services/cloudformation/cloudformation_starter.py", line 260, in _parse_and_create_resource
localstack_1  |     result = deploy_func(logical_id, resource_wrapped, stack_name=stack_name)
localstack_1  |   File "/opt/code/localstack/localstack/utils/cloudformation/template_deployer.py", line 715, in deploy_resource
localstack_1  |     result = deploy_resource_via_sdk_function(resource_id, resources, resource_type, func, stack_name)
localstack_1  |   File "/opt/code/localstack/localstack/utils/cloudformation/template_deployer.py", line 785, in deploy_resource_via_sdk_function
localstack_1  |     raise e
localstack_1  |   File "/opt/code/localstack/localstack/utils/cloudformation/template_deployer.py", line 782, in deploy_resource_via_sdk_function
localstack_1  |     result = function(**params)
localstack_1  |   File "/opt/code/localstack/.venv/lib/python3.7/site-packages/botocore/client.py", line 272, in _api_call
localstack_1  |     return self._make_api_call(operation_name, kwargs)
localstack_1  |   File "/opt/code/localstack/.venv/lib/python3.7/site-packages/botocore/client.py", line 576, in _make_api_call
localstack_1  |     raise error_class(parsed_response, operation_name)
localstack_1  | botocore.exceptions.ClientError: An error occurred (ValidationError) when calling the CreateFunction operation: Unable to find handler script 
```

The last item in this stack trace is the issue:

> botocore.exceptions.ClientError: An error occurred (ValidationError) when calling the CreateFunction operation: Unable to find handler script 

So it looks like it searching for `myHandler.sh` where it's appending that `.sh` or something is and that file doesn't exist so it's dying.

Per the amazon docs it should just be looking for a file name `bootstrap` if the lambda runtime is set to `provided`.

Now it's entirely possible this is an issue in Boto, but jumping to line **576** per the stack trace in the Boto source shows it's just emitting the http error, which leads me to think it's in Localstack .

**... Later ...**

Ok upon further inspection it looks like this if the offending line:

https://github.com/localstack/localstack/blob/master/localstack/services/awslambda/lambda_api.py#L420-L421

It's explicitly affixing that `.sh` extension. May AWS changed things around around how they handle "bootstrap" and at some point it in the past it was "bootstrap.sh" I dunno.. Regardless, I'm thinking the solve, which I will submit if agreed upon, is that for a `runtime` type of `provided` it should be searching for `bootstrap`  no extension at all.  I don't even know that it needs to set the handler at all.. Maybe it does for Localstack.


"""
        Return the Java classpath, using the parent folder of the
        given archive as the base folder.
        The result contains any *.jar files in the base folder, as
        well as any JAR files in the "lib/*" subfolder living
        alongside the supplied java archive (.jar or .zip).
        :param archive: an absolute path to a .jar or .zip Java archive
        :return: the Java classpath, relative to the base dir of "archive"
        """
        entries = ['.']
        base_dir = os.path.dirname(archive)
        for pattern in ['%s/*.jar', '%s/lib/*.jar', '%s/*.zip']:
            for entry in glob.glob(pattern % base_dir):
                if os.path.realpath(archive) != os.path.realpath(entry):
                    entries.append(os.path.relpath(entry, base_dir))
        
        
        entries.append(os.path.relpath(archive, base_dir))
        result = ':'.join(entries)
        return result
EXECUTOR_LOCAL = LambdaExecutorLocal()
EXECUTOR_CONTAINERS_SEPARATE = LambdaExecutorSeparateContainers()
EXECUTOR_CONTAINERS_REUSE = LambdaExecutorReuseContainers()
DEFAULT_EXECUTOR = EXECUTOR_CONTAINERS_SEPARATE
AVAILABLE_EXECUTORS = {
    'local': EXECUTOR_LOCAL,
    'docker': EXECUTOR_CONTAINERS_SEPARATE,
    'docker-reuse': EXECUTOR_CONTAINERS_REUSE
}