We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
ReadTimeoutErrors on 169.254.169.254 /latest/api/token
After updating the agent to version 5.8.0 my all my Django `manage.py` commands seem to hang indefinitely and never finish and I'm first seeing: 

`WARNING  urllib3.connectionpool Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError("HTTPConnectionPool(host='169.254.169.254', port=80): Read timed out. (read timeout=3.0)")': /latest/api/token connectionpool.py:749`

and then (after some time):

`ERROR    elasticapm.transport Closing the transport connection timed out. base.py:261`

After this last error message the process just hangs indefinitely and has to be killed manually.

**To Reproduce**

I'm not able to reproduce this locally, it seems to happen exclusively in my staging/production environment on AWS.
There, it really only takes a Django management command to make it hang. For example:

```
$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  No migrations to apply.
2020-07-03 13:17:19,256 WARNING  urllib3.connectionpool Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError("HTTPConnectionPool(host='169.254.169.254', port=80): Read timed out. (read timeout=3.0)")': /latest/api/token connectionpool.py:749
2020-07-03 13:17:27,164 ERROR    elasticapm.transport Closing the transport connection timed out. base.py:261
```
**Environment**
- OS: `Linux 6094537dd0b9 4.14.181-108.257.amzn1.x86_64 elastic/apm-agent-python#1 SMP Wed May 27 02:43:03 UTC 2020 x86_64 GNU/Linux` (Docker container - python:3.8 image, docker running on Amazon Linux/2.15.2)
- Python version: 3.8.3
- Framework and version: Django 3.0.8 (same thing happens with 3.0.7)
- APM Server version: 7.5.2
- Agent version: 5.8.0 (I can confirm this **doesn't happen with v5.7.0**)


**Additional context**

The app runs on AWS ElasticBeanstalk Docker environment with python 3.8 image, pretty straight forward.

- `requirements.txt`:
  <details>
    <summary>Click to expand</summary>

    ```
    appdirs==1.4.4
    asgiref==3.2.10; python_version >= '3.5'
    boto3==1.14.16
    botocore==1.17.16
    certifi==2020.6.20
    cffi==1.14.0
    chardet==3.0.4
    cryptography==2.9.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
    django-migration-linter==2.3.0
    django-redis==4.12.1
    django-sortedm2m==3.0.0
    django-storages==1.9.1
    django-widget-tweaks==1.4.8
    django==3.0.8
    djangorestframework-simplejwt==4.4.0
    djangorestframework==3.11.0
    docutils==0.15.2; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'
    elastic-apm==5.8.0
    factory-boy==2.12.0
    faker==4.1.1; python_version >= '3.4'
    future==0.18.2; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'
    googlemaps==4.4.1
    gunicorn==20.0.4
    idna==2.10; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
    jmespath==0.10.0; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'
    jwcrypto==0.7
    markdown==3.2.2
    oauthlib==3.1.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
    pillow==7.2.0
    psycopg2-binary==2.8.5
    pycparser==2.20; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
    pyjwt==1.7.1
    python-dateutil==2.8.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
    python-twitter==3.5
    pytz==2020.1
    pyyaml==5.3.1
    redis==3.5.3; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
    requests-oauthlib==1.3.0
    requests==2.24.0
    s3transfer==0.3.3
    sentry-sdk==0.16.0
    six==1.15.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
    sqlparse==0.3.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
    text-unidecode==1.3
    twilio==6.43.0
    ua-parser==0.10.0
    uritemplate==3.0.1
    urllib3==1.25.9; python_version != '3.4'
    user-agents==2.1
    ```
  </details>


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `elasticapm/utils/cloud.py`
Content:
```
1 #  BSD 3-Clause License
2 #
3 #  Copyright (c) 2019, Elasticsearch BV
4 #  All rights reserved.
5 #
6 #  Redistribution and use in source and binary forms, with or without
7 #  modification, are permitted provided that the following conditions are met:
8 #
9 #  * Redistributions of source code must retain the above copyright notice, this
10 #    list of conditions and the following disclaimer.
11 #
12 #  * Redistributions in binary form must reproduce the above copyright notice,
13 #    this list of conditions and the following disclaimer in the documentation
14 #    and/or other materials provided with the distribution.
15 #
16 #  * Neither the name of the copyright holder nor the names of its
17 #    contributors may be used to endorse or promote products derived from
18 #    this software without specific prior written permission.
19 #
20 #  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 #  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 #  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 #  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 #  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 #  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 #  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 #  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 #  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 #  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 import json
32 import os
33 import socket
34 
35 import urllib3
36 
37 
38 def aws_metadata():
39     """
40     Fetch AWS metadata from the local metadata server. If metadata server is
41     not found, return an empty dictionary
42     """
43     http = urllib3.PoolManager()
44 
45     try:
46         # This will throw an error if the metadata server isn't available,
47         # and will be quiet in the logs, unlike urllib3
48         socket.create_connection(("169.254.169.254", 80), 0.1)
49 
50         ttl_header = {"X-aws-ec2-metadata-token-ttl-seconds": "300"}
51         token_url = "http://169.254.169.254/latest/api/token"
52         token_request = http.request("PUT", token_url, headers=ttl_header, timeout=3.0)
53         token = token_request.data.decode("utf-8")
54         aws_token_header = {"X-aws-ec2-metadata-token": token} if token else {}
55         metadata = json.loads(
56             http.request(
57                 "GET",
58                 "http://169.254.169.254/latest/dynamic/instance-identity/document",
59                 headers=aws_token_header,
60                 timeout=3.0,
61                 retries=False,
62             ).data.decode("utf-8")
63         )
64 
65         return {
66             "account": {"id": metadata["accountId"]},
67             "instance": {"id": metadata["instanceId"]},
68             "availability_zone": metadata["availabilityZone"],
69             "machine": {"type": metadata["instanceType"]},
70             "provider": "aws",
71             "region": metadata["region"],
72         }
73 
74     except Exception:
75         # Not on an AWS box
76         return {}
77 
78 
79 def gcp_metadata():
80     """
81     Fetch GCP metadata from the local metadata server. If metadata server is
82     not found, return an empty dictionary
83     """
84     headers = {"Metadata-Flavor": "Google"}
85     http = urllib3.PoolManager()
86 
87     try:
88         # This will throw an error if the metadata server isn't available,
89         # and will be quiet in the logs, unlike urllib3
90         socket.getaddrinfo("metadata.google.internal", 80, 0, socket.SOCK_STREAM)
91 
92         metadata = json.loads(
93             http.request(
94                 "GET",
95                 "http://metadata.google.internal/computeMetadata/v1/?recursive=true",
96                 headers=headers,
97                 timeout=3.0,
98                 retries=False,
99             ).data.decode("utf-8")
100         )
101 
102         availability_zone = os.path.split(metadata["instance"]["zone"])[1]
103 
104         return {
105             "provider": "gcp",
106             "instance": {"id": str(metadata["instance"]["id"]), "name": metadata["instance"]["name"]},
107             "project": {"id": str(metadata["project"]["numericProjectId"]), "name": metadata["project"]["projectId"]},
108             "availability_zone": availability_zone,
109             "region": availability_zone.rsplit("-", 1)[0],
110             "machine": {"type": metadata["instance"]["machineType"]},
111         }
112 
113     except Exception:
114         # Not on a gcp box
115         return {}
116 
117 
118 def azure_metadata():
119     """
120     Fetch Azure metadata from the local metadata server. If metadata server is
121     not found, return an empty dictionary
122     """
123     headers = {"Metadata": "true"}
124     http = urllib3.PoolManager()
125 
126     try:
127         # This will throw an error if the metadata server isn't available,
128         # and will be quiet in the logs, unlike urllib3
129         socket.create_connection(("169.254.169.254", 80), 0.1)
130 
131         # Can't use newest metadata service version, as it's not guaranteed
132         # to be available in all regions
133         metadata = json.loads(
134             http.request(
135                 "GET",
136                 "http://169.254.169.254/metadata/instance/compute?api-version=2019-08-15",
137                 headers=headers,
138                 timeout=3.0,
139                 retries=False,
140             ).data.decode("utf-8")
141         )
142 
143         ret = {
144             "account": {"id": metadata["subscriptionId"]},
145             "instance": {"id": metadata["vmId"], "name": metadata["name"]},
146             "project": {"name": metadata["resourceGroupName"]},
147             "availability_zone": metadata["zone"],
148             "machine": {"type": metadata["vmSize"]},
149             "provider": "azure",
150             "region": metadata["location"],
151         }
152 
153         if not ret["availability_zone"]:
154             ret.pop("availability_zone")
155         return ret
156 
157     except Exception:
158         # Not on an Azure box
159         return {}
160
```
--- 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/elasticapm/utils/cloud.py b/elasticapm/utils/cloud.py
--- a/elasticapm/utils/cloud.py
+++ b/elasticapm/utils/cloud.py
@@ -47,17 +47,26 @@
         # and will be quiet in the logs, unlike urllib3
         socket.create_connection(("169.254.169.254", 80), 0.1)
 
-        ttl_header = {"X-aws-ec2-metadata-token-ttl-seconds": "300"}
-        token_url = "http://169.254.169.254/latest/api/token"
-        token_request = http.request("PUT", token_url, headers=ttl_header, timeout=3.0)
-        token = token_request.data.decode("utf-8")
-        aws_token_header = {"X-aws-ec2-metadata-token": token} if token else {}
+        try:
+            # This whole block is almost unnecessary. IMDSv1 will be supported
+            # indefinitely, so the only time this block is needed is if a
+            # security-conscious user has set the metadata service to require
+            # IMDSv2. Thus, the very expansive try:except: coverage.
+
+            # TODO: should we have a config option to completely disable IMDSv2 to reduce overhead?
+            ttl_header = {"X-aws-ec2-metadata-token-ttl-seconds": "300"}
+            token_url = "http://169.254.169.254/latest/api/token"
+            token_request = http.request("PUT", token_url, headers=ttl_header, timeout=1.0, retries=False)
+            token = token_request.data.decode("utf-8")
+            aws_token_header = {"X-aws-ec2-metadata-token": token} if token else {}
+        except Exception:
+            aws_token_header = {}
         metadata = json.loads(
             http.request(
                 "GET",
                 "http://169.254.169.254/latest/dynamic/instance-identity/document",
                 headers=aws_token_header,
-                timeout=3.0,
+                timeout=1.0,
                 retries=False,
             ).data.decode("utf-8")
         )
@@ -94,7 +103,7 @@
                 "GET",
                 "http://metadata.google.internal/computeMetadata/v1/?recursive=true",
                 headers=headers,
-                timeout=3.0,
+                timeout=1.0,
                 retries=False,
             ).data.decode("utf-8")
         )
@@ -135,7 +144,7 @@
                 "GET",
                 "http://169.254.169.254/metadata/instance/compute?api-version=2019-08-15",
                 headers=headers,
-                timeout=3.0,
+                timeout=1.0,
                 retries=False,
             ).data.decode("utf-8")
         )

{"golden_diff": "diff --git a/elasticapm/utils/cloud.py b/elasticapm/utils/cloud.py\n--- a/elasticapm/utils/cloud.py\n+++ b/elasticapm/utils/cloud.py\n@@ -47,17 +47,26 @@\n         # and will be quiet in the logs, unlike urllib3\n         socket.create_connection((\"169.254.169.254\", 80), 0.1)\n \n-        ttl_header = {\"X-aws-ec2-metadata-token-ttl-seconds\": \"300\"}\n-        token_url = \"http://169.254.169.254/latest/api/token\"\n-        token_request = http.request(\"PUT\", token_url, headers=ttl_header, timeout=3.0)\n-        token = token_request.data.decode(\"utf-8\")\n-        aws_token_header = {\"X-aws-ec2-metadata-token\": token} if token else {}\n+        try:\n+            # This whole block is almost unnecessary. IMDSv1 will be supported\n+            # indefinitely, so the only time this block is needed is if a\n+            # security-conscious user has set the metadata service to require\n+            # IMDSv2. Thus, the very expansive try:except: coverage.\n+\n+            # TODO: should we have a config option to completely disable IMDSv2 to reduce overhead?\n+            ttl_header = {\"X-aws-ec2-metadata-token-ttl-seconds\": \"300\"}\n+            token_url = \"http://169.254.169.254/latest/api/token\"\n+            token_request = http.request(\"PUT\", token_url, headers=ttl_header, timeout=1.0, retries=False)\n+            token = token_request.data.decode(\"utf-8\")\n+            aws_token_header = {\"X-aws-ec2-metadata-token\": token} if token else {}\n+        except Exception:\n+            aws_token_header = {}\n         metadata = json.loads(\n             http.request(\n                 \"GET\",\n                 \"http://169.254.169.254/latest/dynamic/instance-identity/document\",\n                 headers=aws_token_header,\n-                timeout=3.0,\n+                timeout=1.0,\n                 retries=False,\n             ).data.decode(\"utf-8\")\n         )\n@@ -94,7 +103,7 @@\n                 \"GET\",\n                 \"http://metadata.google.internal/computeMetadata/v1/?recursive=true\",\n                 headers=headers,\n-                timeout=3.0,\n+                timeout=1.0,\n                 retries=False,\n             ).data.decode(\"utf-8\")\n         )\n@@ -135,7 +144,7 @@\n                 \"GET\",\n                 \"http://169.254.169.254/metadata/instance/compute?api-version=2019-08-15\",\n                 headers=headers,\n-                timeout=3.0,\n+                timeout=1.0,\n                 retries=False,\n             ).data.decode(\"utf-8\")\n         )\n", "issue": "ReadTimeoutErrors on 169.254.169.254 /latest/api/token\nAfter updating the agent to version 5.8.0 my all my Django `manage.py` commands seem to hang indefinitely and never finish and I'm first seeing: \r\n\r\n`WARNING  urllib3.connectionpool Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError(\"HTTPConnectionPool(host='169.254.169.254', port=80): Read timed out. (read timeout=3.0)\")': /latest/api/token connectionpool.py:749`\r\n\r\nand then (after some time):\r\n\r\n`ERROR    elasticapm.transport Closing the transport connection timed out. base.py:261`\r\n\r\nAfter this last error message the process just hangs indefinitely and has to be killed manually.\r\n\r\n**To Reproduce**\r\n\r\nI'm not able to reproduce this locally, it seems to happen exclusively in my staging/production environment on AWS.\r\nThere, it really only takes a Django management command to make it hang. For example:\r\n\r\n```\r\n$ python manage.py migrate\r\nOperations to perform:\r\n  Apply all migrations: admin, auth, contenttypes, sessions\r\nRunning migrations:\r\n  No migrations to apply.\r\n2020-07-03 13:17:19,256 WARNING  urllib3.connectionpool Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError(\"HTTPConnectionPool(host='169.254.169.254', port=80): Read timed out. (read timeout=3.0)\")': /latest/api/token connectionpool.py:749\r\n2020-07-03 13:17:27,164 ERROR    elasticapm.transport Closing the transport connection timed out. base.py:261\r\n```\r\n**Environment**\r\n- OS: `Linux 6094537dd0b9 4.14.181-108.257.amzn1.x86_64 elastic/apm-agent-python#1 SMP Wed May 27 02:43:03 UTC 2020 x86_64 GNU/Linux` (Docker container - python:3.8 image, docker running on Amazon Linux/2.15.2)\r\n- Python version: 3.8.3\r\n- Framework and version: Django 3.0.8 (same thing happens with 3.0.7)\r\n- APM Server version: 7.5.2\r\n- Agent version: 5.8.0 (I can confirm this **doesn't happen with v5.7.0**)\r\n\r\n\r\n**Additional context**\r\n\r\nThe app runs on AWS ElasticBeanstalk Docker environment with python 3.8 image, pretty straight forward.\r\n\r\n- `requirements.txt`:\r\n  <details>\r\n    <summary>Click to expand</summary>\r\n\r\n    ```\r\n    appdirs==1.4.4\r\n    asgiref==3.2.10; python_version >= '3.5'\r\n    boto3==1.14.16\r\n    botocore==1.17.16\r\n    certifi==2020.6.20\r\n    cffi==1.14.0\r\n    chardet==3.0.4\r\n    cryptography==2.9.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'\r\n    django-migration-linter==2.3.0\r\n    django-redis==4.12.1\r\n    django-sortedm2m==3.0.0\r\n    django-storages==1.9.1\r\n    django-widget-tweaks==1.4.8\r\n    django==3.0.8\r\n    djangorestframework-simplejwt==4.4.0\r\n    djangorestframework==3.11.0\r\n    docutils==0.15.2; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'\r\n    elastic-apm==5.8.0\r\n    factory-boy==2.12.0\r\n    faker==4.1.1; python_version >= '3.4'\r\n    future==0.18.2; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'\r\n    googlemaps==4.4.1\r\n    gunicorn==20.0.4\r\n    idna==2.10; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'\r\n    jmespath==0.10.0; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'\r\n    jwcrypto==0.7\r\n    markdown==3.2.2\r\n    oauthlib==3.1.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'\r\n    pillow==7.2.0\r\n    psycopg2-binary==2.8.5\r\n    pycparser==2.20; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'\r\n    pyjwt==1.7.1\r\n    python-dateutil==2.8.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'\r\n    python-twitter==3.5\r\n    pytz==2020.1\r\n    pyyaml==5.3.1\r\n    redis==3.5.3; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'\r\n    requests-oauthlib==1.3.0\r\n    requests==2.24.0\r\n    s3transfer==0.3.3\r\n    sentry-sdk==0.16.0\r\n    six==1.15.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'\r\n    sqlparse==0.3.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'\r\n    text-unidecode==1.3\r\n    twilio==6.43.0\r\n    ua-parser==0.10.0\r\n    uritemplate==3.0.1\r\n    urllib3==1.25.9; python_version != '3.4'\r\n    user-agents==2.1\r\n    ```\r\n  </details>\r\n\n", "before_files": [{"content": "#  BSD 3-Clause License\n#\n#  Copyright (c) 2019, Elasticsearch BV\n#  All rights reserved.\n#\n#  Redistribution and use in source and binary forms, with or without\n#  modification, are permitted provided that the following conditions are met:\n#\n#  * Redistributions of source code must retain the above copyright notice, this\n#    list of conditions and the following disclaimer.\n#\n#  * Redistributions in binary form must reproduce the above copyright notice,\n#    this list of conditions and the following disclaimer in the documentation\n#    and/or other materials provided with the distribution.\n#\n#  * Neither the name of the copyright holder nor the names of its\n#    contributors may be used to endorse or promote products derived from\n#    this software without specific prior written permission.\n#\n#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n#  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n#  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n#  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n#  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nimport json\nimport os\nimport socket\n\nimport urllib3\n\n\ndef aws_metadata():\n    \"\"\"\n    Fetch AWS metadata from the local metadata server. If metadata server is\n    not found, return an empty dictionary\n    \"\"\"\n    http = urllib3.PoolManager()\n\n    try:\n        # This will throw an error if the metadata server isn't available,\n        # and will be quiet in the logs, unlike urllib3\n        socket.create_connection((\"169.254.169.254\", 80), 0.1)\n\n        ttl_header = {\"X-aws-ec2-metadata-token-ttl-seconds\": \"300\"}\n        token_url = \"http://169.254.169.254/latest/api/token\"\n        token_request = http.request(\"PUT\", token_url, headers=ttl_header, timeout=3.0)\n        token = token_request.data.decode(\"utf-8\")\n        aws_token_header = {\"X-aws-ec2-metadata-token\": token} if token else {}\n        metadata = json.loads(\n            http.request(\n                \"GET\",\n                \"http://169.254.169.254/latest/dynamic/instance-identity/document\",\n                headers=aws_token_header,\n                timeout=3.0,\n                retries=False,\n            ).data.decode(\"utf-8\")\n        )\n\n        return {\n            \"account\": {\"id\": metadata[\"accountId\"]},\n            \"instance\": {\"id\": metadata[\"instanceId\"]},\n            \"availability_zone\": metadata[\"availabilityZone\"],\n            \"machine\": {\"type\": metadata[\"instanceType\"]},\n            \"provider\": \"aws\",\n            \"region\": metadata[\"region\"],\n        }\n\n    except Exception:\n        # Not on an AWS box\n        return {}\n\n\ndef gcp_metadata():\n    \"\"\"\n    Fetch GCP metadata from the local metadata server. If metadata server is\n    not found, return an empty dictionary\n    \"\"\"\n    headers = {\"Metadata-Flavor\": \"Google\"}\n    http = urllib3.PoolManager()\n\n    try:\n        # This will throw an error if the metadata server isn't available,\n        # and will be quiet in the logs, unlike urllib3\n        socket.getaddrinfo(\"metadata.google.internal\", 80, 0, socket.SOCK_STREAM)\n\n        metadata = json.loads(\n            http.request(\n                \"GET\",\n                \"http://metadata.google.internal/computeMetadata/v1/?recursive=true\",\n                headers=headers,\n                timeout=3.0,\n                retries=False,\n            ).data.decode(\"utf-8\")\n        )\n\n        availability_zone = os.path.split(metadata[\"instance\"][\"zone\"])[1]\n\n        return {\n            \"provider\": \"gcp\",\n            \"instance\": {\"id\": str(metadata[\"instance\"][\"id\"]), \"name\": metadata[\"instance\"][\"name\"]},\n            \"project\": {\"id\": str(metadata[\"project\"][\"numericProjectId\"]), \"name\": metadata[\"project\"][\"projectId\"]},\n            \"availability_zone\": availability_zone,\n            \"region\": availability_zone.rsplit(\"-\", 1)[0],\n            \"machine\": {\"type\": metadata[\"instance\"][\"machineType\"]},\n        }\n\n    except Exception:\n        # Not on a gcp box\n        return {}\n\n\ndef azure_metadata():\n    \"\"\"\n    Fetch Azure metadata from the local metadata server. If metadata server is\n    not found, return an empty dictionary\n    \"\"\"\n    headers = {\"Metadata\": \"true\"}\n    http = urllib3.PoolManager()\n\n    try:\n        # This will throw an error if the metadata server isn't available,\n        # and will be quiet in the logs, unlike urllib3\n        socket.create_connection((\"169.254.169.254\", 80), 0.1)\n\n        # Can't use newest metadata service version, as it's not guaranteed\n        # to be available in all regions\n        metadata = json.loads(\n            http.request(\n                \"GET\",\n                \"http://169.254.169.254/metadata/instance/compute?api-version=2019-08-15\",\n                headers=headers,\n                timeout=3.0,\n                retries=False,\n            ).data.decode(\"utf-8\")\n        )\n\n        ret = {\n            \"account\": {\"id\": metadata[\"subscriptionId\"]},\n            \"instance\": {\"id\": metadata[\"vmId\"], \"name\": metadata[\"name\"]},\n            \"project\": {\"name\": metadata[\"resourceGroupName\"]},\n            \"availability_zone\": metadata[\"zone\"],\n            \"machine\": {\"type\": metadata[\"vmSize\"]},\n            \"provider\": \"azure\",\n            \"region\": metadata[\"location\"],\n        }\n\n        if not ret[\"availability_zone\"]:\n            ret.pop(\"availability_zone\")\n        return ret\n\n    except Exception:\n        # Not on an Azure box\n        return {}\n", "path": "elasticapm/utils/cloud.py"}], "after_files": [{"content": "#  BSD 3-Clause License\n#\n#  Copyright (c) 2019, Elasticsearch BV\n#  All rights reserved.\n#\n#  Redistribution and use in source and binary forms, with or without\n#  modification, are permitted provided that the following conditions are met:\n#\n#  * Redistributions of source code must retain the above copyright notice, this\n#    list of conditions and the following disclaimer.\n#\n#  * Redistributions in binary form must reproduce the above copyright notice,\n#    this list of conditions and the following disclaimer in the documentation\n#    and/or other materials provided with the distribution.\n#\n#  * Neither the name of the copyright holder nor the names of its\n#    contributors may be used to endorse or promote products derived from\n#    this software without specific prior written permission.\n#\n#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n#  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n#  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n#  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n#  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nimport json\nimport os\nimport socket\n\nimport urllib3\n\n\ndef aws_metadata():\n    \"\"\"\n    Fetch AWS metadata from the local metadata server. If metadata server is\n    not found, return an empty dictionary\n    \"\"\"\n    http = urllib3.PoolManager()\n\n    try:\n        # This will throw an error if the metadata server isn't available,\n        # and will be quiet in the logs, unlike urllib3\n        socket.create_connection((\"169.254.169.254\", 80), 0.1)\n\n        try:\n            # This whole block is almost unnecessary. IMDSv1 will be supported\n            # indefinitely, so the only time this block is needed is if a\n            # security-conscious user has set the metadata service to require\n            # IMDSv2. Thus, the very expansive try:except: coverage.\n\n            # TODO: should we have a config option to completely disable IMDSv2 to reduce overhead?\n            ttl_header = {\"X-aws-ec2-metadata-token-ttl-seconds\": \"300\"}\n            token_url = \"http://169.254.169.254/latest/api/token\"\n            token_request = http.request(\"PUT\", token_url, headers=ttl_header, timeout=1.0, retries=False)\n            token = token_request.data.decode(\"utf-8\")\n            aws_token_header = {\"X-aws-ec2-metadata-token\": token} if token else {}\n        except Exception:\n            aws_token_header = {}\n        metadata = json.loads(\n            http.request(\n                \"GET\",\n                \"http://169.254.169.254/latest/dynamic/instance-identity/document\",\n                headers=aws_token_header,\n                timeout=1.0,\n                retries=False,\n            ).data.decode(\"utf-8\")\n        )\n\n        return {\n            \"account\": {\"id\": metadata[\"accountId\"]},\n            \"instance\": {\"id\": metadata[\"instanceId\"]},\n            \"availability_zone\": metadata[\"availabilityZone\"],\n            \"machine\": {\"type\": metadata[\"instanceType\"]},\n            \"provider\": \"aws\",\n            \"region\": metadata[\"region\"],\n        }\n\n    except Exception:\n        # Not on an AWS box\n        return {}\n\n\ndef gcp_metadata():\n    \"\"\"\n    Fetch GCP metadata from the local metadata server. If metadata server is\n    not found, return an empty dictionary\n    \"\"\"\n    headers = {\"Metadata-Flavor\": \"Google\"}\n    http = urllib3.PoolManager()\n\n    try:\n        # This will throw an error if the metadata server isn't available,\n        # and will be quiet in the logs, unlike urllib3\n        socket.getaddrinfo(\"metadata.google.internal\", 80, 0, socket.SOCK_STREAM)\n\n        metadata = json.loads(\n            http.request(\n                \"GET\",\n                \"http://metadata.google.internal/computeMetadata/v1/?recursive=true\",\n                headers=headers,\n                timeout=1.0,\n                retries=False,\n            ).data.decode(\"utf-8\")\n        )\n\n        availability_zone = os.path.split(metadata[\"instance\"][\"zone\"])[1]\n\n        return {\n            \"provider\": \"gcp\",\n            \"instance\": {\"id\": str(metadata[\"instance\"][\"id\"]), \"name\": metadata[\"instance\"][\"name\"]},\n            \"project\": {\"id\": str(metadata[\"project\"][\"numericProjectId\"]), \"name\": metadata[\"project\"][\"projectId\"]},\n            \"availability_zone\": availability_zone,\n            \"region\": availability_zone.rsplit(\"-\", 1)[0],\n            \"machine\": {\"type\": metadata[\"instance\"][\"machineType\"]},\n        }\n\n    except Exception:\n        # Not on a gcp box\n        return {}\n\n\ndef azure_metadata():\n    \"\"\"\n    Fetch Azure metadata from the local metadata server. If metadata server is\n    not found, return an empty dictionary\n    \"\"\"\n    headers = {\"Metadata\": \"true\"}\n    http = urllib3.PoolManager()\n\n    try:\n        # This will throw an error if the metadata server isn't available,\n        # and will be quiet in the logs, unlike urllib3\n        socket.create_connection((\"169.254.169.254\", 80), 0.1)\n\n        # Can't use newest metadata service version, as it's not guaranteed\n        # to be available in all regions\n        metadata = json.loads(\n            http.request(\n                \"GET\",\n                \"http://169.254.169.254/metadata/instance/compute?api-version=2019-08-15\",\n                headers=headers,\n                timeout=1.0,\n                retries=False,\n            ).data.decode(\"utf-8\")\n        )\n\n        ret = {\n            \"account\": {\"id\": metadata[\"subscriptionId\"]},\n            \"instance\": {\"id\": metadata[\"vmId\"], \"name\": metadata[\"name\"]},\n            \"project\": {\"name\": metadata[\"resourceGroupName\"]},\n            \"availability_zone\": metadata[\"zone\"],\n            \"machine\": {\"type\": metadata[\"vmSize\"]},\n            \"provider\": \"azure\",\n            \"region\": metadata[\"location\"],\n        }\n\n        if not ret[\"availability_zone\"]:\n            ret.pop(\"availability_zone\")\n        return ret\n\n    except Exception:\n        # Not on an Azure box\n        return {}\n", "path": "elasticapm/utils/cloud.py"}]}