Repository: elastic/apm-agent-python

Issue:
Cloud metadata collection is leaking sockets
When collecting the meta data information from cloud providers a socket connection is opened but not closed. This causes `ResourceWarnings` when running pytest. The issue seems to be the socket that is opened [here](https://github.com/elastic/apm-agent-python/blob/master/elasticapm/utils/cloud.py#L138).

**To Reproduce**
Create a file `test.py` with the following content:

```python
import elasticapm

def test():
    c = elasticapm.Client({"SERVICE_NAME": "dev"})
    c.build_metadata()
    assert False
```

Run the tests with:
```cmd
pytest --pythonwarnings once test.py
```

You should see warnings similar to this one (you have to run this on a cloud hosted VM):
```cmd
test.py::test
  /tmp/venv/lib/python3.6/site-packages/elasticapm/utils/cloud.py:48: ResourceWarning: unclosed <socket.socket fd=11, family=AddressFamily.AF_INET, type=2049, proto=6, laddr=('10.5.128.4', 52428), raddr=('169.254.169.254', 80)>
    socket.create_connection(("169.254.169.254", 80), 0.1)

test.py::test
  /tmp/venv/lib/python3.6/site-packages/elasticapm/utils/cloud.py:48: ResourceWarning: unclosed <socket.socket fd=12, family=AddressFamily.AF_INET, type=2049, proto=6, laddr=('10.5.128.4', 52430), raddr=('169.254.169.254', 80)>
    socket.create_connection(("169.254.169.254", 80), 0.1)

test.py::test
  /tmp/venv/lib/python3.6/site-packages/elasticapm/base.py:393: ResourceWarning: unclosed <socket.socket fd=11, family=AddressFamily.AF_INET, type=2049, proto=6, laddr=('10.5.128.4', 52432), raddr=('169.254.169.254', 80)>
    data = cloud.aws_metadata()

test.py::test
  /tmp/venv/lib/python3.6/site-packages/elasticapm/base.py:393: ResourceWarning: unclosed <socket.socket fd=12, family=AddressFamily.AF_INET, type=2049, proto=6, laddr=('10.5.128.4', 52434), raddr=('169.254.169.254', 80)>
    data = cloud.aws_metadata()

test.py::test
  /tmp/venv/lib/python3.6/site-packages/elasticapm/utils/cloud.py:138: ResourceWarning: unclosed <socket.socket fd=12, family=AddressFamily.AF_INET, type=2049, proto=6, laddr=('10.5.128.4', 52436), raddr=('169.254.169.254', 80)>
    socket.create_connection(("169.254.169.254", 80), 0.1)

test.py::test
  /tmp/venv/lib/python3.6/site-packages/elasticapm/utils/cloud.py:138: ResourceWarning: unclosed <socket.socket fd=11, family=AddressFamily.AF_INET, type=2049, proto=6, laddr=('10.5.128.4', 52438), raddr=('169.254.169.254', 80)>
    socket.create_connection(("169.254.169.254", 80), 0.1)
```
**Environment (please complete the following information)**
- OS: Mac and Linux
- Python version: 3.6.6
- APM Server version:
- Agent version: 5.10.0


Discussion:

Resolving patch:
diff --git a/elasticapm/utils/cloud.py b/elasticapm/utils/cloud.py
--- a/elasticapm/utils/cloud.py
+++ b/elasticapm/utils/cloud.py
@@ -45,7 +45,8 @@ def aws_metadata():
     try:
         # This will throw an error if the metadata server isn't available,
         # and will be quiet in the logs, unlike urllib3
-        socket.create_connection(("169.254.169.254", 80), 0.1)
+        with socket.create_connection(("169.254.169.254", 80), 0.1):
+            pass
 
         try:
             # This whole block is almost unnecessary. IMDSv1 will be supported
@@ -135,7 +136,8 @@ def azure_metadata():
     try:
         # This will throw an error if the metadata server isn't available,
         # and will be quiet in the logs, unlike urllib3
-        socket.create_connection(("169.254.169.254", 80), 0.1)
+        with socket.create_connection(("169.254.169.254", 80), 0.1):
+            pass
 
         # Can't use newest metadata service version, as it's not guaranteed
         # to be available in all regions


Test patch: