Repository: localstack/localstack

Issue:
An error occurred (BadRequestException) when calling the CreateDeployment operation: The REST API doesn't contain any methods
Hi team, 

I got new issue when trying using localstack@latest docker and localstack version 0.10.6. 

Run with command: `SLS_DEBUG=true sls deploy --stage=local`

localstack logs:
```
2019-12-31T08:22:22:WARNING:moto: No Moto CloudFormation support for AWS::S3::BucketPolicy
2019-12-31T08:22:27:WARNING:localstack.services.awslambda.lambda_api: Function not found: arn:aws:lambda:us-east-1:000000000000:function:publicspam-local-world
2019-12-31T08:22:28:INFO:localstack.services.cloudformation.cloudformation_starter: Updating resource ID from eefcf5b3 to pkqefv1g9n (us-east-1)
2019-12-31T08:22:28:WARNING:localstack.services.cloudformation.cloudformation_starter: Unable to determine physical_resource_id for resource <class 'moto.apigateway.models.RestAPI'>
2019-12-31T08:22:28:INFO:localstack.services.cloudformation.cloudformation_starter: Updating resource ID from 3a7ff272 to vxcggbi30s (us-east-1)
2019-12-31T08:22:28:WARNING:localstack.services.cloudformation.cloudformation_starter: Unable to determine physical_resource_id for resource <class 'moto.apigateway.models.Resource'>
2019-12-31T08:22:28:WARNING:localstack.utils.cloudformation.template_deployer: Error calling <bound method ClientCreator._create_api_method.<locals>._api_call of <botocore.client.APIGateway object at 0x7fdcab360e50>> with params: {'restApiId': 'pkqefv1g9n', 'stageName': 'local'} for resource: {'Type': 'AWS::ApiGateway::Deployment', 'Properties': {'RestApiId': 'pkqefv1g9n', 'StageName': 'local'}, 'DependsOn': ['ApiGatewayMethodWorldGet']}
2019-12-31T08:22:28:ERROR:localstack.services.cloudformation.cloudformation_starter: Unable to parse and create resource "ApiGatewayDeployment1577780537704": An error occurred (BadRequestException) when calling the CreateDeployment operation: The REST API doesn't contain any methods Traceback (most recent call last):
  File "/opt/code/localstack/localstack/services/cloudformation/cloudformation_starter.py", line 178, in parse_and_create_resource
    return _parse_and_create_resource(logical_id, resource_json, resources_map, region_name)
  File "/opt/code/localstack/localstack/services/cloudformation/cloudformation_starter.py", line 265, in _parse_and_create_resource
    result = deploy_func(logical_id, resource_wrapped, stack_name=stack_name)
  File "/opt/code/localstack/localstack/utils/cloudformation/template_deployer.py", line 723, in deploy_resource
    return execute_resource_action(resource_id, resources, stack_name, ACTION_CREATE)
  File "/opt/code/localstack/localstack/utils/cloudformation/template_deployer.py", line 749, in execute_resource_action
    result = configure_resource_via_sdk(resource_id, resources, resource_type, func, stack_name)
  File "/opt/code/localstack/localstack/utils/cloudformation/template_deployer.py", line 819, in configure_resource_via_sdk
    raise e
  File "/opt/code/localstack/localstack/utils/cloudformation/template_deployer.py", line 816, in configure_resource_via_sdk
    result = function(**params)
  File "/opt/code/localstack/.venv/lib/python3.7/site-packages/botocore/client.py", line 276, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/opt/code/localstack/.venv/lib/python3.7/site-packages/botocore/client.py", line 586, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.errorfactory.BadRequestException: An error occurred (BadRequestException) when calling the CreateDeployment operation: The REST API doesn't contain any methods

```
My env from sls:
```
Your Environment Information ---------------------------
     Operating System:          darwin
     Node Version:              12.13.1
     Framework Version:         1.58.0
     Plugin Version:            3.2.5
     SDK Version:               2.2.1
     Components Core Version:   1.1.2
     Components CLI Version:    1.4.0
```

Thanks,



Discussion:
Thanks for reporting @nguyenhoai890 . Can you please share a minimal `serverless.yml` configuration that helps reproduce this issue? Thanks
> Thanks for reporting @nguyenhoai890 . Can you please share a minimal `serverless.yml` configuration that helps reproduce this issue? Thanks

Hi @whummer, srry for my late reply.
I just used the sample on the web:
```
service: demo

frameworkVersion: '>=1.28.0 <2.0.0'

plugins:
  - serverless-localstack

provider:
  name: aws
  runtime: go1.x

package:
  exclude:
    - ./**
  include:
    - ./bin/**

functions:
  world:
    handler: bin/world
    events:
      - http:
          path: world
          method: get
custom:
  localstack:
    debug: true
#    lambda:
#      mountCode: true
    stages:
      - local
    host: http://localhost
```
I have exactly the same problem.  It sometimes goes away when I restart localstack, but only about 10% of the time.  My serverless.yml

```
service: foo

provider:
  name: aws
  runtime: nodejs12.x

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: post


plugins:
  - serverless-localstack

custom:
  localstack:
    stages:
      - local   
```

.. I'm seeing this as well. Would love to know if there's a workaround.

Resolving patch:
diff --git a/localstack/services/cloudformation/cloudformation_starter.py b/localstack/services/cloudformation/cloudformation_starter.py
--- a/localstack/services/cloudformation/cloudformation_starter.py
+++ b/localstack/services/cloudformation/cloudformation_starter.py
@@ -219,8 +219,9 @@ def _parse_and_create_resource(logical_id, resource_json, resources_map, region_
             return resource
 
         # check whether this resource needs to be deployed
-        resource_wrapped = {logical_id: resource_json}
-        should_be_created = template_deployer.should_be_deployed(logical_id, resource_wrapped, stack_name)
+        resource_map_new = dict(resources_map._resource_json_map)
+        resource_map_new[logical_id] = resource_json
+        should_be_created = template_deployer.should_be_deployed(logical_id, resource_map_new, stack_name)
 
         # fix resource ARNs, make sure to convert account IDs 000000000000 to 123456789012
         resource_json_arns_fixed = clone(json_safe(convert_objs_to_ids(resource_json)))
@@ -250,7 +251,7 @@ def _parse_and_create_resource(logical_id, resource_json, resources_map, region_
         is_updateable = False
         if not should_be_created:
             # This resource is either not deployable or already exists. Check if it can be updated
-            is_updateable = template_deployer.is_updateable(logical_id, resource_wrapped, stack_name)
+            is_updateable = template_deployer.is_updateable(logical_id, resource_map_new, stack_name)
             if not update or not is_updateable:
                 LOG.debug('Resource %s need not be deployed: %s %s' % (logical_id, resource_json, bool(resource)))
                 # Return if this resource already exists and can/need not be updated
@@ -264,7 +265,7 @@ def _parse_and_create_resource(logical_id, resource_json, resources_map, region_
         try:
             CURRENTLY_UPDATING_RESOURCES[resource_hash_key] = True
             deploy_func = template_deployer.update_resource if update else template_deployer.deploy_resource
-            result = deploy_func(logical_id, resource_wrapped, stack_name=stack_name)
+            result = deploy_func(logical_id, resource_map_new, stack_name=stack_name)
         finally:
             CURRENTLY_UPDATING_RESOURCES[resource_hash_key] = False
 
diff --git a/localstack/utils/cloudformation/template_deployer.py b/localstack/utils/cloudformation/template_deployer.py
--- a/localstack/utils/cloudformation/template_deployer.py
+++ b/localstack/utils/cloudformation/template_deployer.py
@@ -871,6 +871,7 @@ def configure_resource_via_sdk(resource_id, resources, resource_type, func_detai
     return result
 
 
+# TODO remove?
 def deploy_template(template, stack_name):
     if isinstance(template, string_types):
         template = parse_template(template)
@@ -957,7 +958,7 @@ def all_dependencies_satisfied(resources, stack_name, all_resources, depending_r
 
 def resources_to_deploy_next(resources, stack_name):
     result = {}
-    for resource_id, resource in iteritems(resources):
+    for resource_id, resource in resources.items():
         if should_be_deployed(resource_id, resources, stack_name):
             result[resource_id] = resource
     return result
@@ -967,7 +968,7 @@ def get_resource_dependencies(resource_id, resource, resources):
     result = {}
     dumped = json.dumps(common.json_safe(resource))
     dependencies = resource.get('DependsOn', [])
-    for other_id, other in iteritems(resources):
+    for other_id, other in resources.items():
         if resource != other:
             # TODO: traverse dict instead of doing string search
             search1 = '{"Ref": "%s"}' % other_id


Test patch: