Skip to content

Commit

Permalink
add manual publications of ports
Browse files Browse the repository at this point in the history
  • Loading branch information
jzelinskie committed Apr 6, 2020
1 parent 01ef31a commit 6a931d4
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 8 deletions.
23 changes: 17 additions & 6 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,23 @@ your test suite as it runs, as ordinary environment variables::
Host and Port Mapping
---------------------

tox-docker runs docker with the "publish all ports" option. Any port the
container exposes will be made available to your test suite via environment
variables of the form ``<image-basename>_<exposed-port>_<protocol>_PORT``.
For instance, for the PostgreSQL container, there will be an environment
variable ``POSTGRES_5432_TCP_PORT`` whose value is the ephemeral port number
that docker has bound the container's port 5432 to.
By default, tox-docker runs the container with the "publish all ports" option.
You may also specify port publishing in ``tox.ini``, in a new section like::

[docker:redis:5.0-alpine]
ports = 5432:5432/tcp

The image name -- everything after the ``docker:`` in the section header --
must *exactly* match the image name used in your testenv's ``docker`` setting.
Published ports are separated by a newline and are in the format
``<HOST>:<CONTAINER>/<PROTOCOL>``.

Any port the container exposes will be made available to your test suite via
environment variables of the form
``<image-basename>_<exposed-port>_<protocol>_PORT``. For instance, for the
PostgreSQL container, there will be an environment variable
``POSTGRES_5432_TCP_PORT`` whose value is the ephemeral port number that docker
has bound the container's port 5432 to.

Likewise, exposed UDP ports will have environment variables like
``TELEGRAF_8092_UDP_PORT`` Since it's not possible to check whether UDP port
Expand Down
18 changes: 18 additions & 0 deletions test_ports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import unittest

import docker


class ToxDockerPortTest(unittest.TestCase):

def test_it_exposes_only_specified_port(self):
client = docker.from_env(version="auto")
mysql_container = None
for container in client.containers.list()
if "mysql" in containers.attrs["Config"]["Image"]:
mysql_container = container
break

self.assertIsNotNone(mysql_container, "could not find mysql container")
self.assertIsNotNone(mysql_container.attrs["NetworkSettings"]["Ports"].get("3306/tcp"))
self.assertIsNone(mysql_container.attrs["NetworkSettings"]["Ports"].get("33060/tcp"))
10 changes: 9 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = integration,registry,healthcheck-builtin,healthcheck-custom
envlist = integration,registry,healthcheck-builtin,healthcheck-custom,ports

[testenv]
# commands_pre/_post only work in tox 3.4+, but at least in some
Expand Down Expand Up @@ -46,3 +46,11 @@ healthcheck_interval = 1
healthcheck_timeout = 1
healthcheck_retries = 30
healthcheck_start_period = 0.5

[testenv:ports]
docker = mysql:5.7
deps = pytest
commands = py.test [] test_ports.py

[docker:mysql:5.7]
ports = 3306:3306/tcp
28 changes: 27 additions & 1 deletion tox_docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,25 @@ def getint(reader, key):
"healthcheck_timeout": gettime(reader, "healthcheck_timeout"),
"healthcheck_retries": getint(reader, "healthcheck_retries"),
"healthcheck_start_period": gettime(reader, "healthcheck_start_period"),
"ports": reader.getlist("ports"),
}

config._docker_image_configs = image_configs


def _validate_port(port_line):
host_port, _, container_port_proto = port_line.partition(":")
host_port = int(host_port)

container_port, _, protocol = container_port_proto.partition("/")
container_port = int(container_port)

if protocol.lower() not in ("tcp", "udp"):
raise ValueError("protocol is not tcp or udp")

return (host_port, container_port_proto)


@hookimpl
def tox_runtest_pre(venv):
envconfig = venv.envconfig
Expand Down Expand Up @@ -163,12 +177,20 @@ def tox_runtest_pre(venv):
else:
healthcheck = None

ports = {}
for port_mapping in image_config.get("ports", []):
host_port, container_port_proto = _validate_port(port_mapping)
existing_ports = set(ports.get(container_port_proto, []))
existing_ports.add(host_port)
ports[container_port_proto] = list(existing_ports)

action.setactivity("docker", "run {!r}".format(image))
with action:
container = docker.containers.run(
image,
detach=True,
publish_all_ports=True,
publish_all_ports=len(ports) == 0,
ports=ports,
environment=environment,
healthcheck=healthcheck,
)
Expand All @@ -195,6 +217,10 @@ def tox_runtest_pre(venv):
name, _, tag = image.partition(":")
gateway_ip = _get_gateway_ip(container)
for containerport, hostports in container.attrs["NetworkSettings"]["Ports"].items():
if hostports is None:
# The port is exposed by the container, but not published.
continue

for spec in hostports:
if spec["HostIp"] == "0.0.0.0":
hostport = spec["HostPort"]
Expand Down

0 comments on commit 6a931d4

Please sign in to comment.