We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Delete EC2 formation leaves garbage at EC2
You can sucessufully delete a EC2 formation, but later you have no way to create another with the same ID, as there will still exist a Security Group and Key Pair with same name on EC2


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `provider/ec2.py`
Content:
```
1 """
2 Deis cloud provider implementation for Amazon EC2.
3 """
4 
5 from __future__ import unicode_literals
6 
7 import json
8 import time
9 
10 from boto import ec2
11 from boto.exception import EC2ResponseError
12 
13 # from api.ssh import connect_ssh, exec_ssh
14 from deis import settings
15 
16 
17 # Deis-optimized EC2 amis -- with 3.8 kernel, chef 11 deps,
18 # and large docker images (e.g. buildstep) pre-installed
19 IMAGE_MAP = {
20     'ap-northeast-1': 'ami-6399f962',
21     'ap-southeast-1': 'ami-0a87d358',
22     'ap-southeast-2': 'ami-c3bd22f9',
23     'eu-west-1': 'ami-4826c83f',
24     'sa-east-1': 'ami-79bf1e64',
25     'us-east-1': 'ami-e7af828e',
26     'us-west-1': 'ami-a06e5ee5',
27     'us-west-2': 'ami-28abce18',
28 }
29 
30 
31 def seed_flavors():
32     """Seed the database with default flavors for each EC2 region.
33 
34     :rtype: list of dicts containing flavor data
35     """
36     flavors = []
37     for r in ('us-east-1', 'us-west-1', 'us-west-2', 'eu-west-1',
38               'ap-northeast-1', 'ap-southeast-1', 'ap-southeast-2',
39               'sa-east-1'):
40         flavors.append({'id': 'ec2-{}'.format(r),
41                         'provider': 'ec2',
42                         'params': json.dumps({
43                             'region': r,
44                             'image': IMAGE_MAP[r],
45                             'zone': 'any',
46                             'size': 'm1.medium'})})
47     return flavors
48 
49 
50 def build_layer(layer):
51     """
52     Build a layer.
53 
54     :param layer: a dict containing formation, id, params, and creds info
55     """
56     region = layer['params'].get('region', 'us-east-1')
57     conn = _create_ec2_connection(layer['creds'], region)
58     # create a new sg and authorize all ports
59     # use iptables on the host to firewall ports
60     name = "{formation}-{id}".format(**layer)
61     sg = conn.create_security_group(name, 'Created by Deis')
62     # import a new keypair using the layer key material
63     conn.import_key_pair(name, layer['ssh_public_key'])
64     # loop until the sg is *actually* there
65     for i in xrange(10):
66         try:
67             sg.authorize(ip_protocol='tcp', from_port=1, to_port=65535,
68                          cidr_ip='0.0.0.0/0')
69             break
70         except EC2ResponseError:
71             if i < 10:
72                 time.sleep(1.5)
73                 continue
74             else:
75                 raise RuntimeError('Failed to authorize security group')
76 
77 
78 def destroy_layer(layer):
79     """
80     Destroy a layer.
81 
82     :param layer: a dict containing formation, id, params, and creds info
83     """
84     region = layer['params'].get('region', 'us-east-1')
85     name = "{formation}-{id}".format(**layer)
86     conn = _create_ec2_connection(layer['creds'], region)
87     conn.delete_key_pair(name)
88     # there's an ec2 race condition on instances terminating
89     # successfully but still holding a lock on the security group
90     # let's take a nap
91     time.sleep(5)
92     try:
93         conn.delete_security_group(name)
94     except EC2ResponseError as e:
95         if e.code != 'InvalidGroup.NotFound':
96             raise e
97 
98 
99 def build_node(node):
100     """
101     Build a node.
102 
103     :param node: a dict containing formation, layer, params, and creds info.
104     :rtype: a tuple of (provider_id, fully_qualified_domain_name, metadata)
105     """
106     params, creds = node['params'], node['creds']
107     region = params.setdefault('region', 'us-east-1')
108     conn = _create_ec2_connection(creds, region)
109     name = "{formation}-{layer}".format(**node)
110     params['key_name'] = name
111     sg = conn.get_all_security_groups(name)[0]
112     params.setdefault('security_groups', []).append(sg.name)
113     image_id = params.get(
114         'image', getattr(settings, 'IMAGE_MAP', IMAGE_MAP)[region])
115     images = conn.get_all_images([image_id])
116     if len(images) != 1:
117         raise LookupError('Could not find AMI: %s' % image_id)
118     image = images[0]
119     kwargs = _prepare_run_kwargs(params)
120     reservation = image.run(**kwargs)
121     instances = reservation.instances
122     boto = instances[0]
123     # sleep before tagging
124     time.sleep(10)
125     boto.update()
126     boto.add_tag('Name', node['id'])
127     # loop until running
128     while(True):
129         time.sleep(2)
130         boto.update()
131         if boto.state == 'running':
132             break
133     # prepare return values
134     provider_id = boto.id
135     fqdn = boto.public_dns_name
136     metadata = _format_metadata(boto)
137     return provider_id, fqdn, metadata
138 
139 
140 def destroy_node(node):
141     """
142     Destroy a node.
143 
144     :param node: a dict containing a node's provider_id, params, and creds
145     """
146     provider_id = node['provider_id']
147     region = node['params'].get('region', 'us-east-1')
148     conn = _create_ec2_connection(node['creds'], region)
149     if provider_id:
150         try:
151             conn.terminate_instances([provider_id])
152             i = conn.get_all_instances([provider_id])[0].instances[0]
153             while(True):
154                 time.sleep(2)
155                 i.update()
156                 if i.state == "terminated":
157                     break
158         except EC2ResponseError as e:
159             if e.code not in ('InvalidInstanceID.NotFound',):
160                 raise
161 
162 
163 def _create_ec2_connection(creds, region):
164     """
165     Connect to an EC2 region with the given credentials.
166 
167     :param creds: a dict containing an EC2 access_key and secret_key
168     :region: the name of an EC2 region, such as "us-west-2"
169     :rtype: a connected :class:`~boto.ec2.connection.EC2Connection`
170     :raises EnvironmentError: if no credentials are provided
171     """
172     if not creds:
173         raise EnvironmentError('No credentials provided')
174     return ec2.connect_to_region(region,
175                                  aws_access_key_id=creds['access_key'],
176                                  aws_secret_access_key=creds['secret_key'])
177 
178 
179 def _prepare_run_kwargs(params):
180     # start with sane defaults
181     kwargs = {
182         'min_count': 1, 'max_count': 1,
183         'user_data': None, 'addressing_type': None,
184         'instance_type': None, 'placement': None,
185         'kernel_id': None, 'ramdisk_id': None,
186         'monitoring_enabled': False, 'subnet_id': None,
187         'block_device_map': None,
188     }
189     # convert zone "any" to NoneType
190     requested_zone = params.get('zone')
191     if requested_zone and requested_zone.lower() == 'any':
192         requested_zone = None
193     # lookup kwargs from params
194     param_kwargs = {
195         'instance_type': params.get('size', 'm1.medium'),
196         'security_groups': params['security_groups'],
197         'placement': requested_zone,
198         'key_name': params['key_name'],
199         'kernel_id': params.get('kernel', None),
200     }
201     # add user_data if provided in params
202     user_data = params.get('user_data')
203     if user_data:
204         kwargs.update({'user_data': user_data})
205     # params override defaults
206     kwargs.update(param_kwargs)
207     return kwargs
208 
209 
210 def _format_metadata(boto):
211     return {
212         'architecture': boto.architecture,
213         'block_device_mapping': {
214             k: v.volume_id for k, v in boto.block_device_mapping.items()
215         },
216         'client_token': boto.client_token,
217         'dns_name': boto.dns_name,
218         'ebs_optimized': boto.ebs_optimized,
219         'eventsSet': boto.eventsSet,
220         'group_name': boto.group_name,
221         'groups': [g.id for g in boto.groups],
222         'hypervisor': boto.hypervisor,
223         'id': boto.id,
224         'image_id': boto.image_id,
225         'instance_profile': boto.instance_profile,
226         'instance_type': boto.instance_type,
227         'interfaces': list(boto.interfaces),
228         'ip_address': boto.ip_address,
229         'kernel': boto.kernel,
230         'key_name': boto.key_name,
231         'launch_time': boto.launch_time,
232         'monitored': boto.monitored,
233         'monitoring_state': boto.monitoring_state,
234         'persistent': boto.persistent,
235         'placement': boto.placement,
236         'placement_group': boto.placement_group,
237         'placement_tenancy': boto.placement_tenancy,
238         'previous_state': boto.previous_state,
239         'private_dns_name': boto.private_dns_name,
240         'private_ip_address': boto.private_ip_address,
241         'public_dns_name': boto.public_dns_name,
242         'ramdisk': boto.ramdisk,
243         'region': boto.region.name,
244         'root_device_name': boto.root_device_name,
245         'root_device_type': boto.root_device_type,
246         'spot_instance_request_id': boto.spot_instance_request_id,
247         'state': boto.state,
248         'state_code': boto.state_code,
249         'state_reason': boto.state_reason,
250         'subnet_id': boto.subnet_id,
251         'tags': dict(boto.tags),
252         'virtualization_type': boto.virtualization_type,
253         'vpc_id': boto.vpc_id,
254     }
255
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/provider/ec2.py b/provider/ec2.py
--- a/provider/ec2.py
+++ b/provider/ec2.py
@@ -87,13 +87,20 @@
     conn.delete_key_pair(name)
     # there's an ec2 race condition on instances terminating
     # successfully but still holding a lock on the security group
-    # let's take a nap
-    time.sleep(5)
-    try:
-        conn.delete_security_group(name)
-    except EC2ResponseError as e:
-        if e.code != 'InvalidGroup.NotFound':
-            raise e
+    for i in range(5):
+        # let's take a nap
+        time.sleep(i ** 1.25)  # 1, 2.4, 3.9, 5.6, 7.4
+        try:
+            conn.delete_security_group(name)
+            return
+        except EC2ResponseError as err:
+            if err.code == 'InvalidGroup.NotFound':
+                return
+            elif err.code in ('InvalidGroup.InUse',
+                              'DependencyViolation') and i < 4:
+                continue  # retry
+            else:
+                raise
 
 
 def build_node(node):

{"golden_diff": "diff --git a/provider/ec2.py b/provider/ec2.py\n--- a/provider/ec2.py\n+++ b/provider/ec2.py\n@@ -87,13 +87,20 @@\n     conn.delete_key_pair(name)\n     # there's an ec2 race condition on instances terminating\n     # successfully but still holding a lock on the security group\n-    # let's take a nap\n-    time.sleep(5)\n-    try:\n-        conn.delete_security_group(name)\n-    except EC2ResponseError as e:\n-        if e.code != 'InvalidGroup.NotFound':\n-            raise e\n+    for i in range(5):\n+        # let's take a nap\n+        time.sleep(i ** 1.25)  # 1, 2.4, 3.9, 5.6, 7.4\n+        try:\n+            conn.delete_security_group(name)\n+            return\n+        except EC2ResponseError as err:\n+            if err.code == 'InvalidGroup.NotFound':\n+                return\n+            elif err.code in ('InvalidGroup.InUse',\n+                              'DependencyViolation') and i < 4:\n+                continue  # retry\n+            else:\n+                raise\n \n \n def build_node(node):\n", "issue": "Delete EC2 formation leaves garbage at EC2\nYou can sucessufully delete a EC2 formation, but later you have no way to create another with the same ID, as there will still exist a Security Group and Key Pair with same name on EC2\n\n", "before_files": [{"content": "\"\"\"\nDeis cloud provider implementation for Amazon EC2.\n\"\"\"\n\nfrom __future__ import unicode_literals\n\nimport json\nimport time\n\nfrom boto import ec2\nfrom boto.exception import EC2ResponseError\n\n# from api.ssh import connect_ssh, exec_ssh\nfrom deis import settings\n\n\n# Deis-optimized EC2 amis -- with 3.8 kernel, chef 11 deps,\n# and large docker images (e.g. buildstep) pre-installed\nIMAGE_MAP = {\n    'ap-northeast-1': 'ami-6399f962',\n    'ap-southeast-1': 'ami-0a87d358',\n    'ap-southeast-2': 'ami-c3bd22f9',\n    'eu-west-1': 'ami-4826c83f',\n    'sa-east-1': 'ami-79bf1e64',\n    'us-east-1': 'ami-e7af828e',\n    'us-west-1': 'ami-a06e5ee5',\n    'us-west-2': 'ami-28abce18',\n}\n\n\ndef seed_flavors():\n    \"\"\"Seed the database with default flavors for each EC2 region.\n\n    :rtype: list of dicts containing flavor data\n    \"\"\"\n    flavors = []\n    for r in ('us-east-1', 'us-west-1', 'us-west-2', 'eu-west-1',\n              'ap-northeast-1', 'ap-southeast-1', 'ap-southeast-2',\n              'sa-east-1'):\n        flavors.append({'id': 'ec2-{}'.format(r),\n                        'provider': 'ec2',\n                        'params': json.dumps({\n                            'region': r,\n                            'image': IMAGE_MAP[r],\n                            'zone': 'any',\n                            'size': 'm1.medium'})})\n    return flavors\n\n\ndef build_layer(layer):\n    \"\"\"\n    Build a layer.\n\n    :param layer: a dict containing formation, id, params, and creds info\n    \"\"\"\n    region = layer['params'].get('region', 'us-east-1')\n    conn = _create_ec2_connection(layer['creds'], region)\n    # create a new sg and authorize all ports\n    # use iptables on the host to firewall ports\n    name = \"{formation}-{id}\".format(**layer)\n    sg = conn.create_security_group(name, 'Created by Deis')\n    # import a new keypair using the layer key material\n    conn.import_key_pair(name, layer['ssh_public_key'])\n    # loop until the sg is *actually* there\n    for i in xrange(10):\n        try:\n            sg.authorize(ip_protocol='tcp', from_port=1, to_port=65535,\n                         cidr_ip='0.0.0.0/0')\n            break\n        except EC2ResponseError:\n            if i < 10:\n                time.sleep(1.5)\n                continue\n            else:\n                raise RuntimeError('Failed to authorize security group')\n\n\ndef destroy_layer(layer):\n    \"\"\"\n    Destroy a layer.\n\n    :param layer: a dict containing formation, id, params, and creds info\n    \"\"\"\n    region = layer['params'].get('region', 'us-east-1')\n    name = \"{formation}-{id}\".format(**layer)\n    conn = _create_ec2_connection(layer['creds'], region)\n    conn.delete_key_pair(name)\n    # there's an ec2 race condition on instances terminating\n    # successfully but still holding a lock on the security group\n    # let's take a nap\n    time.sleep(5)\n    try:\n        conn.delete_security_group(name)\n    except EC2ResponseError as e:\n        if e.code != 'InvalidGroup.NotFound':\n            raise e\n\n\ndef build_node(node):\n    \"\"\"\n    Build a node.\n\n    :param node: a dict containing formation, layer, params, and creds info.\n    :rtype: a tuple of (provider_id, fully_qualified_domain_name, metadata)\n    \"\"\"\n    params, creds = node['params'], node['creds']\n    region = params.setdefault('region', 'us-east-1')\n    conn = _create_ec2_connection(creds, region)\n    name = \"{formation}-{layer}\".format(**node)\n    params['key_name'] = name\n    sg = conn.get_all_security_groups(name)[0]\n    params.setdefault('security_groups', []).append(sg.name)\n    image_id = params.get(\n        'image', getattr(settings, 'IMAGE_MAP', IMAGE_MAP)[region])\n    images = conn.get_all_images([image_id])\n    if len(images) != 1:\n        raise LookupError('Could not find AMI: %s' % image_id)\n    image = images[0]\n    kwargs = _prepare_run_kwargs(params)\n    reservation = image.run(**kwargs)\n    instances = reservation.instances\n    boto = instances[0]\n    # sleep before tagging\n    time.sleep(10)\n    boto.update()\n    boto.add_tag('Name', node['id'])\n    # loop until running\n    while(True):\n        time.sleep(2)\n        boto.update()\n        if boto.state == 'running':\n            break\n    # prepare return values\n    provider_id = boto.id\n    fqdn = boto.public_dns_name\n    metadata = _format_metadata(boto)\n    return provider_id, fqdn, metadata\n\n\ndef destroy_node(node):\n    \"\"\"\n    Destroy a node.\n\n    :param node: a dict containing a node's provider_id, params, and creds\n    \"\"\"\n    provider_id = node['provider_id']\n    region = node['params'].get('region', 'us-east-1')\n    conn = _create_ec2_connection(node['creds'], region)\n    if provider_id:\n        try:\n            conn.terminate_instances([provider_id])\n            i = conn.get_all_instances([provider_id])[0].instances[0]\n            while(True):\n                time.sleep(2)\n                i.update()\n                if i.state == \"terminated\":\n                    break\n        except EC2ResponseError as e:\n            if e.code not in ('InvalidInstanceID.NotFound',):\n                raise\n\n\ndef _create_ec2_connection(creds, region):\n    \"\"\"\n    Connect to an EC2 region with the given credentials.\n\n    :param creds: a dict containing an EC2 access_key and secret_key\n    :region: the name of an EC2 region, such as \"us-west-2\"\n    :rtype: a connected :class:`~boto.ec2.connection.EC2Connection`\n    :raises EnvironmentError: if no credentials are provided\n    \"\"\"\n    if not creds:\n        raise EnvironmentError('No credentials provided')\n    return ec2.connect_to_region(region,\n                                 aws_access_key_id=creds['access_key'],\n                                 aws_secret_access_key=creds['secret_key'])\n\n\ndef _prepare_run_kwargs(params):\n    # start with sane defaults\n    kwargs = {\n        'min_count': 1, 'max_count': 1,\n        'user_data': None, 'addressing_type': None,\n        'instance_type': None, 'placement': None,\n        'kernel_id': None, 'ramdisk_id': None,\n        'monitoring_enabled': False, 'subnet_id': None,\n        'block_device_map': None,\n    }\n    # convert zone \"any\" to NoneType\n    requested_zone = params.get('zone')\n    if requested_zone and requested_zone.lower() == 'any':\n        requested_zone = None\n    # lookup kwargs from params\n    param_kwargs = {\n        'instance_type': params.get('size', 'm1.medium'),\n        'security_groups': params['security_groups'],\n        'placement': requested_zone,\n        'key_name': params['key_name'],\n        'kernel_id': params.get('kernel', None),\n    }\n    # add user_data if provided in params\n    user_data = params.get('user_data')\n    if user_data:\n        kwargs.update({'user_data': user_data})\n    # params override defaults\n    kwargs.update(param_kwargs)\n    return kwargs\n\n\ndef _format_metadata(boto):\n    return {\n        'architecture': boto.architecture,\n        'block_device_mapping': {\n            k: v.volume_id for k, v in boto.block_device_mapping.items()\n        },\n        'client_token': boto.client_token,\n        'dns_name': boto.dns_name,\n        'ebs_optimized': boto.ebs_optimized,\n        'eventsSet': boto.eventsSet,\n        'group_name': boto.group_name,\n        'groups': [g.id for g in boto.groups],\n        'hypervisor': boto.hypervisor,\n        'id': boto.id,\n        'image_id': boto.image_id,\n        'instance_profile': boto.instance_profile,\n        'instance_type': boto.instance_type,\n        'interfaces': list(boto.interfaces),\n        'ip_address': boto.ip_address,\n        'kernel': boto.kernel,\n        'key_name': boto.key_name,\n        'launch_time': boto.launch_time,\n        'monitored': boto.monitored,\n        'monitoring_state': boto.monitoring_state,\n        'persistent': boto.persistent,\n        'placement': boto.placement,\n        'placement_group': boto.placement_group,\n        'placement_tenancy': boto.placement_tenancy,\n        'previous_state': boto.previous_state,\n        'private_dns_name': boto.private_dns_name,\n        'private_ip_address': boto.private_ip_address,\n        'public_dns_name': boto.public_dns_name,\n        'ramdisk': boto.ramdisk,\n        'region': boto.region.name,\n        'root_device_name': boto.root_device_name,\n        'root_device_type': boto.root_device_type,\n        'spot_instance_request_id': boto.spot_instance_request_id,\n        'state': boto.state,\n        'state_code': boto.state_code,\n        'state_reason': boto.state_reason,\n        'subnet_id': boto.subnet_id,\n        'tags': dict(boto.tags),\n        'virtualization_type': boto.virtualization_type,\n        'vpc_id': boto.vpc_id,\n    }\n", "path": "provider/ec2.py"}], "after_files": [{"content": "\"\"\"\nDeis cloud provider implementation for Amazon EC2.\n\"\"\"\n\nfrom __future__ import unicode_literals\n\nimport json\nimport time\n\nfrom boto import ec2\nfrom boto.exception import EC2ResponseError\n\n# from api.ssh import connect_ssh, exec_ssh\nfrom deis import settings\n\n\n# Deis-optimized EC2 amis -- with 3.8 kernel, chef 11 deps,\n# and large docker images (e.g. buildstep) pre-installed\nIMAGE_MAP = {\n    'ap-northeast-1': 'ami-6399f962',\n    'ap-southeast-1': 'ami-0a87d358',\n    'ap-southeast-2': 'ami-c3bd22f9',\n    'eu-west-1': 'ami-4826c83f',\n    'sa-east-1': 'ami-79bf1e64',\n    'us-east-1': 'ami-e7af828e',\n    'us-west-1': 'ami-a06e5ee5',\n    'us-west-2': 'ami-28abce18',\n}\n\n\ndef seed_flavors():\n    \"\"\"Seed the database with default flavors for each EC2 region.\n\n    :rtype: list of dicts containing flavor data\n    \"\"\"\n    flavors = []\n    for r in ('us-east-1', 'us-west-1', 'us-west-2', 'eu-west-1',\n              'ap-northeast-1', 'ap-southeast-1', 'ap-southeast-2',\n              'sa-east-1'):\n        flavors.append({'id': 'ec2-{}'.format(r),\n                        'provider': 'ec2',\n                        'params': json.dumps({\n                            'region': r,\n                            'image': IMAGE_MAP[r],\n                            'zone': 'any',\n                            'size': 'm1.medium'})})\n    return flavors\n\n\ndef build_layer(layer):\n    \"\"\"\n    Build a layer.\n\n    :param layer: a dict containing formation, id, params, and creds info\n    \"\"\"\n    region = layer['params'].get('region', 'us-east-1')\n    conn = _create_ec2_connection(layer['creds'], region)\n    # create a new sg and authorize all ports\n    # use iptables on the host to firewall ports\n    name = \"{formation}-{id}\".format(**layer)\n    sg = conn.create_security_group(name, 'Created by Deis')\n    # import a new keypair using the layer key material\n    conn.import_key_pair(name, layer['ssh_public_key'])\n    # loop until the sg is *actually* there\n    for i in xrange(10):\n        try:\n            sg.authorize(ip_protocol='tcp', from_port=1, to_port=65535,\n                         cidr_ip='0.0.0.0/0')\n            break\n        except EC2ResponseError:\n            if i < 10:\n                time.sleep(1.5)\n                continue\n            else:\n                raise RuntimeError('Failed to authorize security group')\n\n\ndef destroy_layer(layer):\n    \"\"\"\n    Destroy a layer.\n\n    :param layer: a dict containing formation, id, params, and creds info\n    \"\"\"\n    region = layer['params'].get('region', 'us-east-1')\n    name = \"{formation}-{id}\".format(**layer)\n    conn = _create_ec2_connection(layer['creds'], region)\n    conn.delete_key_pair(name)\n    # there's an ec2 race condition on instances terminating\n    # successfully but still holding a lock on the security group\n    for i in range(5):\n        # let's take a nap\n        time.sleep(i ** 1.25)  # 1, 2.4, 3.9, 5.6, 7.4\n        try:\n            conn.delete_security_group(name)\n            return\n        except EC2ResponseError as err:\n            if err.code == 'InvalidGroup.NotFound':\n                return\n            elif err.code in ('InvalidGroup.InUse',\n                              'DependencyViolation') and i < 4:\n                continue  # retry\n            else:\n                raise\n\n\ndef build_node(node):\n    \"\"\"\n    Build a node.\n\n    :param node: a dict containing formation, layer, params, and creds info.\n    :rtype: a tuple of (provider_id, fully_qualified_domain_name, metadata)\n    \"\"\"\n    params, creds = node['params'], node['creds']\n    region = params.setdefault('region', 'us-east-1')\n    conn = _create_ec2_connection(creds, region)\n    name = \"{formation}-{layer}\".format(**node)\n    params['key_name'] = name\n    sg = conn.get_all_security_groups(name)[0]\n    params.setdefault('security_groups', []).append(sg.name)\n    image_id = params.get(\n        'image', getattr(settings, 'IMAGE_MAP', IMAGE_MAP)[region])\n    images = conn.get_all_images([image_id])\n    if len(images) != 1:\n        raise LookupError('Could not find AMI: %s' % image_id)\n    image = images[0]\n    kwargs = _prepare_run_kwargs(params)\n    reservation = image.run(**kwargs)\n    instances = reservation.instances\n    boto = instances[0]\n    # sleep before tagging\n    time.sleep(10)\n    boto.update()\n    boto.add_tag('Name', node['id'])\n    # loop until running\n    while(True):\n        time.sleep(2)\n        boto.update()\n        if boto.state == 'running':\n            break\n    # prepare return values\n    provider_id = boto.id\n    fqdn = boto.public_dns_name\n    metadata = _format_metadata(boto)\n    return provider_id, fqdn, metadata\n\n\ndef destroy_node(node):\n    \"\"\"\n    Destroy a node.\n\n    :param node: a dict containing a node's provider_id, params, and creds\n    \"\"\"\n    provider_id = node['provider_id']\n    region = node['params'].get('region', 'us-east-1')\n    conn = _create_ec2_connection(node['creds'], region)\n    if provider_id:\n        try:\n            conn.terminate_instances([provider_id])\n            i = conn.get_all_instances([provider_id])[0].instances[0]\n            while(True):\n                time.sleep(2)\n                i.update()\n                if i.state == \"terminated\":\n                    break\n        except EC2ResponseError as e:\n            if e.code not in ('InvalidInstanceID.NotFound',):\n                raise\n\n\ndef _create_ec2_connection(creds, region):\n    \"\"\"\n    Connect to an EC2 region with the given credentials.\n\n    :param creds: a dict containing an EC2 access_key and secret_key\n    :region: the name of an EC2 region, such as \"us-west-2\"\n    :rtype: a connected :class:`~boto.ec2.connection.EC2Connection`\n    :raises EnvironmentError: if no credentials are provided\n    \"\"\"\n    if not creds:\n        raise EnvironmentError('No credentials provided')\n    return ec2.connect_to_region(region,\n                                 aws_access_key_id=creds['access_key'],\n                                 aws_secret_access_key=creds['secret_key'])\n\n\ndef _prepare_run_kwargs(params):\n    # start with sane defaults\n    kwargs = {\n        'min_count': 1, 'max_count': 1,\n        'user_data': None, 'addressing_type': None,\n        'instance_type': None, 'placement': None,\n        'kernel_id': None, 'ramdisk_id': None,\n        'monitoring_enabled': False, 'subnet_id': None,\n        'block_device_map': None,\n    }\n    # convert zone \"any\" to NoneType\n    requested_zone = params.get('zone')\n    if requested_zone and requested_zone.lower() == 'any':\n        requested_zone = None\n    # lookup kwargs from params\n    param_kwargs = {\n        'instance_type': params.get('size', 'm1.medium'),\n        'security_groups': params['security_groups'],\n        'placement': requested_zone,\n        'key_name': params['key_name'],\n        'kernel_id': params.get('kernel', None),\n    }\n    # add user_data if provided in params\n    user_data = params.get('user_data')\n    if user_data:\n        kwargs.update({'user_data': user_data})\n    # params override defaults\n    kwargs.update(param_kwargs)\n    return kwargs\n\n\ndef _format_metadata(boto):\n    return {\n        'architecture': boto.architecture,\n        'block_device_mapping': {\n            k: v.volume_id for k, v in boto.block_device_mapping.items()\n        },\n        'client_token': boto.client_token,\n        'dns_name': boto.dns_name,\n        'ebs_optimized': boto.ebs_optimized,\n        'eventsSet': boto.eventsSet,\n        'group_name': boto.group_name,\n        'groups': [g.id for g in boto.groups],\n        'hypervisor': boto.hypervisor,\n        'id': boto.id,\n        'image_id': boto.image_id,\n        'instance_profile': boto.instance_profile,\n        'instance_type': boto.instance_type,\n        'interfaces': list(boto.interfaces),\n        'ip_address': boto.ip_address,\n        'kernel': boto.kernel,\n        'key_name': boto.key_name,\n        'launch_time': boto.launch_time,\n        'monitored': boto.monitored,\n        'monitoring_state': boto.monitoring_state,\n        'persistent': boto.persistent,\n        'placement': boto.placement,\n        'placement_group': boto.placement_group,\n        'placement_tenancy': boto.placement_tenancy,\n        'previous_state': boto.previous_state,\n        'private_dns_name': boto.private_dns_name,\n        'private_ip_address': boto.private_ip_address,\n        'public_dns_name': boto.public_dns_name,\n        'ramdisk': boto.ramdisk,\n        'region': boto.region.name,\n        'root_device_name': boto.root_device_name,\n        'root_device_type': boto.root_device_type,\n        'spot_instance_request_id': boto.spot_instance_request_id,\n        'state': boto.state,\n        'state_code': boto.state_code,\n        'state_reason': boto.state_reason,\n        'subnet_id': boto.subnet_id,\n        'tags': dict(boto.tags),\n        'virtualization_type': boto.virtualization_type,\n        'vpc_id': boto.vpc_id,\n    }\n", "path": "provider/ec2.py"}]}