Repository: ansible/ansible

Issue:
An error occurred (RequestLimitExceeded) when calling the DescribeVpcEndpoints operation 
An error occurred (RequestLimitExceeded) when calling the DescribeVpcEndpoints operation 

ISSUE TYPE
Bug Report

COMPONENT NAME
ec2_vpc_endpoint_facts

ANSIBLE VERSION
```
ansible 2.4.2.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.5 (default, Aug  4 2017, 00:39:18) [GCC 4.8.5 20150623 (Red Hat 4.8.5-16)]
```

CONFIGURATION
DEFAULT_ROLES_PATH(/etc/ansible/ansible.cfg) = [u'/etc/ansible/roles', u'/usr/share/ansible/roles']

 OS / ENVIRONMENT
Centos7.4

SUMMARY
Received this error while trying to collect  VPC endpoint facts.

STEPS TO REPRODUCE
1. Provision AWS VPC,  Subnets 
2. Collect VPC endpoint facts -> Throws RateLimitExceeded error

```
- name: EC2 VPC | Gather VPC s3 endpoint facts
  ec2_vpc_endpoint_facts:
    query: endpoints
    region: "{{ net_data.network_region }}"
    filters:
      vpc-id: "{{ vpc.vpc.id }}"
      vpc-endpoint-state:
        - available
      service-name: "com.amazonaws.{{ net_data.network_region }}.s3"
  register: existing_s3_endpoints

- name: EC2 VPC | Create a VPC s3 endpoint, only if doesn't exist
  ec2_vpc_endpoint:
    state: present
    region: "{{ net_data.network_region }}"
    vpc_id: "{{ vpc.vpc.id }}"
    service: "com.amazonaws.{{ net_data.network_region }}.s3"
  register: vpc_s3_endpoint
  when: existing_s3_endpoints.vpc_endpoints == []
```
EXPECTED RESULTS
VPC endpoint facts should have been fetched succssfully.

##### ACTUAL RESULTS
Receiving request limit exceeded error.
```
 "raw_output": {"localhost": {"_ansible_no_log": false, "_ansible_parsed": false, "changed": false, "exception": "Traceback (most recent call last):\n File \"/tmp/ansible_3EyYlb/ansible_module_ec2_vpc_endpoint_facts.py\", line 205, in <module>\n main()\n File \"/tmp/ansible_3EyYlb/ansible_module_ec2_vpc_endpoint_facts.py\", line 199, in main\n results = invocations[module.params.get('query')](connection, module)\n File \"/tmp/ansible_3EyYlb/ansible_module_ec2_vpc_endpoint_facts.py\", line 157, in get_endpoints\n response = client.describe_vpc_endpoints(*params)\n File \"/usr/lib/python2.7/site-packages/botocore/client.py\", line 312, in _api_call\n return self._make_api_call(operation_name, kwargs)\n File \"/usr/lib/python2.7/site-packages/botocore/client.py\", line 601, in _make_api_call\n raise error_class(parsed_response, operation_name)\nbotocore.exceptions.ClientError: An error occurred (RequestLimitExceeded) when calling the DescribeVpcEndpoints operation (reached max retries: 4): Request limit exceeded.\n", "failed": true, "module_stderr": "Traceback (most recent call last):\n File \"/tmp/ansible_3EyYlb/ansible_module_ec2_vpc_endpoint_facts.py\", line 205, in <module>\n main()\n File \"/tmp/ansible_3EyYlb/ansible_module_ec2_vpc_endpoint_facts.py\", line 199, in main\n results = invocations[module.params.get('query')](connection, module)\n File \"/tmp/ansible_3EyYlb/ansible_module_ec2_vpc_endpoint_facts.py\", line 157, in get_endpoints\n response = client.describe_vpc_endpoints(*params)\n File \"/usr/lib/python2.7/site-packages/botocore/client.py\", line 312, in _api_call\n return self._make_api_call(operation_name, kwargs)\n File \"/usr/lib/python2.7/site-packages/botocore/client.py\", line 601, in _make_api_call\n raise error_class(parsed_response, operation_name)\nbotocore.exceptions.ClientError: An error occurred (RequestLimitExceeded) when calling the DescribeVpcEndpoints operation (reached max retries: 4): Request limit exceeded.\n", "module_stdout": "", "msg": "MODULE FAILURE", "rc": 0}}, "task": {"id": "06164f46-5166-9c6c-b611-00000000005c", "name": "EC2 VPC | Gather VPC s3 endpoint facts", "start": 1513760461.687647}}
```



Discussion:
Files identified in the description:
* [lib/ansible/modules/cloud/amazon/ec2_vpc_endpoint_facts.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/cloud/amazon/ec2_vpc_endpoint_facts.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @Etherdaemon @willthames
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/lib/ansible/modules/cloud/amazon/ec2_vpc_endpoint_facts.py b/lib/ansible/modules/cloud/amazon/ec2_vpc_endpoint_facts.py
--- a/lib/ansible/modules/cloud/amazon/ec2_vpc_endpoint_facts.py
+++ b/lib/ansible/modules/cloud/amazon/ec2_vpc_endpoint_facts.py
@@ -118,13 +118,14 @@
 
 from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.ec2 import (ec2_argument_spec, boto3_conn, get_aws_connection_info,
-                                      ansible_dict_to_boto3_filter_list, HAS_BOTO3, camel_dict_to_snake_dict)
+                                      ansible_dict_to_boto3_filter_list, HAS_BOTO3, camel_dict_to_snake_dict, AWSRetry)
 
 
 def date_handler(obj):
     return obj.isoformat() if hasattr(obj, 'isoformat') else obj
 
 
+@AWSRetry.exponential_backoff()
 def get_supported_services(client, module):
     results = list()
     params = dict()
@@ -138,6 +139,7 @@ def get_supported_services(client, module):
     return dict(service_names=results)
 
 
+@AWSRetry.exponential_backoff()
 def get_endpoints(client, module):
     results = list()
     params = dict()


Test patch: