Merge branch 'fix/host-network-mode'
Some checks failed
Docker / Test (push) Has been cancelled
Docker / Tests-E2E-Docker (push) Has been cancelled
Docker / Tests-E2E-Kubernetes (push) Has been cancelled
Docker / Tests-E2E-Additional (push) Has been cancelled
Docker / Tests-E2E-Swarm (push) Has been cancelled
Docker / Build (push) Has been cancelled
Docker / Publish-PyPI (push) Has been cancelled
Docker / Helm (push) Has been cancelled
Docker / HelmDeploy (push) Has been cancelled
Docker / Documentation (push) Has been cancelled
Some checks failed
Docker / Test (push) Has been cancelled
Docker / Tests-E2E-Docker (push) Has been cancelled
Docker / Tests-E2E-Kubernetes (push) Has been cancelled
Docker / Tests-E2E-Additional (push) Has been cancelled
Docker / Tests-E2E-Swarm (push) Has been cancelled
Docker / Build (push) Has been cancelled
Docker / Publish-PyPI (push) Has been cancelled
Docker / Helm (push) Has been cancelled
Docker / HelmDeploy (push) Has been cancelled
Docker / Documentation (push) Has been cancelled
This commit is contained in:
commit
1302425d09
8 changed files with 399 additions and 17 deletions
|
|
@ -8,9 +8,7 @@ sidebar_label: "Limitations"
|
|||
## EasyHAProxy will not work with --network=host
|
||||
|
||||
:::danger Network Mode Incompatibility
|
||||
The `--network=host` option **cannot** be used with EasyHAProxy due to its networking requirements.
|
||||
|
||||
EasyHAProxy needs to inspect and interact with Docker containers from within the Docker network where it's running. Using the `--network=host` option bypasses Docker networking, preventing EasyHAProxy from accessing and configuring containers effectively.
|
||||
The `--network=host` option **cannot** be used with the EasyHAProxy container itself due to its networking requirements.
|
||||
:::
|
||||
|
||||
## Considerations for Multiple Replica Deployments in EasyHAProxy
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ EasyHAProxy inspects running Docker containers, reads their labels, and configur
|
|||
|
||||
:::warning Limitations
|
||||
- You cannot mix Docker containers with Swarm containers.
|
||||
- This method does not work with containers that use the `--network=host` option. See [limitations](../concepts/limitations.md) for details.
|
||||
- EasyHAProxy itself cannot run with the `--network=host` option. See [limitations](../concepts/limitations.md) for details.
|
||||
:::
|
||||
|
||||
## Step 1 — Create a shared network
|
||||
|
|
@ -49,6 +49,32 @@ docker run -d \
|
|||
|
||||
EasyHAProxy detects this container automatically and routes traffic from `example.org:80` to port 8080 in your container. You do not need to expose any container ports.
|
||||
|
||||
### Containers using `--network=host`
|
||||
|
||||
Containers that share the host network namespace (`--network=host`, or `network_mode: host` in
|
||||
Compose) cannot join the EasyHAProxy network, so EasyHAProxy reaches them through the gateway of
|
||||
its own network instead. Label them as usual, but set `localport` to the port the service binds
|
||||
**on the host**:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
myapp:
|
||||
image: my/image:tag
|
||||
network_mode: host
|
||||
labels:
|
||||
easyhaproxy.myapp.host: example.org
|
||||
easyhaproxy.myapp.port: 80
|
||||
easyhaproxy.myapp.localport: 8080 # the port on the host
|
||||
```
|
||||
|
||||
The same applies to containers sharing another container's namespace
|
||||
(`network_mode: "container:xxx"`, as used by VPN sidecars); those are reached at the address of
|
||||
the container owning the namespace.
|
||||
|
||||
The service must bind `0.0.0.0` rather than `127.0.0.1`, otherwise it is unreachable from the
|
||||
EasyHAProxy container. If your host firewall blocks the Docker bridge, or the detected gateway is
|
||||
not the address you want, override it with `EASYHAPROXY_HOST_NETWORK_IP`.
|
||||
|
||||
## Step 4 — Verify
|
||||
|
||||
Open `http://example.org` in your browser (or `curl http://example.org`). Traffic should reach your container.
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ sidebar_label: "Environment Variables"
|
|||
|---------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------|
|
||||
| EASYHAPROXY_DISCOVER | How the services will be discovered to create `haproxy.cfg`: `static`, `docker`, `swarm` or `kubernetes` | **required** |
|
||||
| EASYHAPROXY_LABEL_PREFIX | (Optional) The key will search for matching resources. | `easyhaproxy` |
|
||||
| EASYHAPROXY_HOST_NETWORK_IP | (Optional) Address used to reach containers running with `--network=host`. Defaults to the gateway of the EasyHAProxy network. | *auto* |
|
||||
| EASYHAPROXY_BASE_PATH | (Optional) Base directory for all EasyHAProxy files. All paths (config, certs, plugins, www) are constructed relative to this base. | `/etc/easyhaproxy` |
|
||||
| EASYHAPROXY_CERTBOT_* | (Optional) Enable Let's Encrypt or any other ACME certificate. See more: [acme](../guides/acme.md) | *empty* |
|
||||
| EASYHAPROXY_SSL_MODE | (Optional) `strict` supports only the most recent TLS version; `default` good SSL integration with recent browsers; `loose` supports all old SSL protocols for old browsers (not recommended). | `default` |
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ class ContainerEnv:
|
|||
env_vars["lookup_label"] = os.getenv("EASYHAPROXY_LABEL_PREFIX") if os.getenv(
|
||||
"EASYHAPROXY_LABEL_PREFIX") else "easyhaproxy"
|
||||
|
||||
env_vars["host_network_ip"] = os.getenv("EASYHAPROXY_HOST_NETWORK_IP", "")
|
||||
|
||||
env_vars["logLevel"] = {
|
||||
"easyhaproxy": os.getenv("EASYHAPROXY_LOG_LEVEL") if os.getenv(
|
||||
"EASYHAPROXY_LOG_LEVEL") else Functions.DEBUG,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import ipaddress
|
||||
import socket
|
||||
|
||||
import docker
|
||||
|
||||
from functions import ContainerEnv, logger_easyhaproxy
|
||||
|
||||
from .interface import ProcessorInterface
|
||||
|
||||
|
||||
|
|
@ -12,27 +15,145 @@ class Docker(ProcessorInterface):
|
|||
super().__init__()
|
||||
|
||||
def inspect_network(self):
|
||||
self.parsed_object = {}
|
||||
own_container = None
|
||||
try:
|
||||
ha_proxy_network_name = next(
|
||||
iter(self.client.containers.get(socket.gethostname()).attrs["NetworkSettings"]["Networks"]))
|
||||
own_container = self.client.containers.get(socket.gethostname())
|
||||
ha_proxy_network_name = next(iter(own_container.attrs["NetworkSettings"]["Networks"]))
|
||||
except Exception:
|
||||
# HAProxy is not running in a container, get first container network
|
||||
if len(self.client.containers.list()) == 0:
|
||||
own_container = None
|
||||
ha_proxy_network_name = self._first_attachable_network()
|
||||
if ha_proxy_network_name is None:
|
||||
return
|
||||
ha_proxy_network_name = next(iter(
|
||||
self.client.containers.get(self.client.containers.list()[0].name).attrs["NetworkSettings"]["Networks"]))
|
||||
|
||||
ha_proxy_network = self.client.networks.get(ha_proxy_network_name)
|
||||
host_address = self._host_gateway_address(own_container, ha_proxy_network, ha_proxy_network_name)
|
||||
|
||||
self.parsed_object = {}
|
||||
for container in self.client.containers.list():
|
||||
if not any(self.label in key for key in container.labels):
|
||||
continue
|
||||
|
||||
# Issue 32 - Docker container cannot connect to containers in different network.
|
||||
if ha_proxy_network_name not in container.attrs["NetworkSettings"]["Networks"].keys():
|
||||
ha_proxy_network.connect(container.name)
|
||||
container = self.client.containers.get(container.name) # refresh object
|
||||
ip_address = self._container_address(container, ha_proxy_network, ha_proxy_network_name, host_address)
|
||||
if ip_address is None:
|
||||
continue
|
||||
self._merge_labels(ip_address, container)
|
||||
|
||||
ip_address = container.attrs["NetworkSettings"]["Networks"][ha_proxy_network_name]["IPAddress"]
|
||||
self.parsed_object[ip_address] = container.labels
|
||||
def _first_attachable_network(self):
|
||||
# Containers sharing a network namespace have no network of their own to borrow.
|
||||
for container in self.client.containers.list():
|
||||
if self._network_mode(container) in ["host", "none"] or self._namespace_owner_id(container) is not None:
|
||||
continue
|
||||
networks = container.attrs["NetworkSettings"]["Networks"] or {}
|
||||
if networks:
|
||||
return next(iter(networks))
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _network_mode(container):
|
||||
return (container.attrs.get("HostConfig") or {}).get("NetworkMode", "")
|
||||
|
||||
@staticmethod
|
||||
def _namespace_owner_id(container):
|
||||
network_mode = Docker._network_mode(container)
|
||||
if network_mode.startswith("container:"):
|
||||
return network_mode.split(":", 1)[1]
|
||||
return None
|
||||
|
||||
def _container_address(self, container, ha_proxy_network, ha_proxy_network_name, host_address, seen=None):
|
||||
"""
|
||||
Resolve the address HAProxy must use to reach this container.
|
||||
Returns None when the container cannot be reached.
|
||||
"""
|
||||
# Issue 47 - Containers sharing the host network namespace cannot join any other network.
|
||||
if self._network_mode(container) == "host":
|
||||
if host_address is None:
|
||||
logger_easyhaproxy.warning(
|
||||
f"Container '{container.name}' uses the host network, but the address of the host could not be "
|
||||
f"determined. Set EASYHAPROXY_HOST_NETWORK_IP to fix it. Skipping it."
|
||||
)
|
||||
return host_address
|
||||
|
||||
owner_id = self._namespace_owner_id(container)
|
||||
if owner_id is not None:
|
||||
return self._namespace_owner_address(
|
||||
container, owner_id, ha_proxy_network, ha_proxy_network_name, host_address, seen
|
||||
)
|
||||
|
||||
networks = container.attrs["NetworkSettings"]["Networks"] or {}
|
||||
# Issue 32 - Docker container cannot connect to containers in different network.
|
||||
if ha_proxy_network_name not in networks.keys():
|
||||
try:
|
||||
ha_proxy_network.connect(container.name)
|
||||
except docker.errors.APIError as e:
|
||||
logger_easyhaproxy.warning(
|
||||
f"Container '{container.name}' could not be connected to the network "
|
||||
f"'{ha_proxy_network_name}': {e}. Skipping it."
|
||||
)
|
||||
return None
|
||||
container = self.client.containers.get(container.name) # refresh object
|
||||
networks = container.attrs["NetworkSettings"]["Networks"] or {}
|
||||
|
||||
return (networks.get(ha_proxy_network_name) or {}).get("IPAddress") or None
|
||||
|
||||
def _namespace_owner_address(self, container, owner_id, ha_proxy_network, ha_proxy_network_name, host_address,
|
||||
seen=None):
|
||||
"""
|
||||
Containers started with `network_mode: container:xxx` are reachable at the address of the
|
||||
container owning the network namespace.
|
||||
"""
|
||||
seen = seen or set()
|
||||
if container.id in seen:
|
||||
logger_easyhaproxy.warning(
|
||||
f"Container '{container.name}' has a circular network namespace reference. Skipping it."
|
||||
)
|
||||
return None
|
||||
seen.add(container.id)
|
||||
|
||||
try:
|
||||
owner = self.client.containers.get(owner_id)
|
||||
except docker.errors.NotFound:
|
||||
logger_easyhaproxy.warning(
|
||||
f"Container '{container.name}' shares the network namespace of '{owner_id}', which was not found. "
|
||||
f"Skipping it."
|
||||
)
|
||||
return None
|
||||
|
||||
return self._container_address(owner, ha_proxy_network, ha_proxy_network_name, host_address, seen)
|
||||
|
||||
def _host_gateway_address(self, own_container, ha_proxy_network, ha_proxy_network_name):
|
||||
"""
|
||||
Address the host is reachable at from inside the HAProxy network. Every container sharing the
|
||||
host network namespace is served through it.
|
||||
"""
|
||||
host_network_ip = ContainerEnv.read()["host_network_ip"]
|
||||
if host_network_ip:
|
||||
return host_network_ip
|
||||
|
||||
if own_container is not None:
|
||||
gateway = (own_container.attrs["NetworkSettings"]["Networks"].get(ha_proxy_network_name) or {}).get(
|
||||
"Gateway")
|
||||
if gateway:
|
||||
return gateway
|
||||
|
||||
for config in (ha_proxy_network.attrs.get("IPAM") or {}).get("Config") or []:
|
||||
if config.get("Gateway"):
|
||||
return config["Gateway"]
|
||||
try:
|
||||
return str(next(ipaddress.ip_network(config["Subnet"]).hosts()))
|
||||
except (KeyError, ValueError, StopIteration):
|
||||
continue
|
||||
|
||||
return None
|
||||
|
||||
def _merge_labels(self, ip_address, container):
|
||||
# Every container sharing the host network namespace resolves to the same address, so the
|
||||
# labels are merged instead of replaced.
|
||||
labels = self.parsed_object.setdefault(ip_address, {})
|
||||
for key, value in container.labels.items():
|
||||
if key.startswith(self.label) and key in labels and labels[key] != value:
|
||||
logger_easyhaproxy.warning(
|
||||
f"Container '{container.name}' redefines '{key}' for '{ip_address}' as '{value}' instead of "
|
||||
f"'{labels[key]}'. Give each container a unique definition name."
|
||||
)
|
||||
labels.update(container.labels)
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ _CONTAINER_ENV_VARS = [
|
|||
"HAPROXY_CUSTOMERRORS",
|
||||
"EASYHAPROXY_SSL_MODE",
|
||||
"EASYHAPROXY_LABEL_PREFIX",
|
||||
"EASYHAPROXY_HOST_NETWORK_IP",
|
||||
"EASYHAPROXY_LOG_LEVEL",
|
||||
"HAPROXY_LOG_LEVEL",
|
||||
"CERTBOT_LOG_LEVEL",
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ def test_container_env_empty():
|
|||
"customerrors": False,
|
||||
"ssl_mode": "default",
|
||||
"lookup_label": "easyhaproxy",
|
||||
"host_network_ip": "",
|
||||
"logLevel": {
|
||||
"easyhaproxy": Functions.DEBUG,
|
||||
"haproxy": Functions.INFO,
|
||||
|
|
@ -42,6 +43,7 @@ def test_container_env_customerrors():
|
|||
"customerrors": True,
|
||||
"ssl_mode": "default",
|
||||
"lookup_label": "easyhaproxy",
|
||||
"host_network_ip": "",
|
||||
"logLevel": {
|
||||
"easyhaproxy": Functions.DEBUG,
|
||||
"haproxy": Functions.INFO,
|
||||
|
|
@ -76,6 +78,7 @@ def test_container_env_sslmode():
|
|||
"customerrors": False,
|
||||
"ssl_mode": "strict",
|
||||
"lookup_label": "easyhaproxy",
|
||||
"host_network_ip": "",
|
||||
"logLevel": {
|
||||
"easyhaproxy": Functions.DEBUG,
|
||||
"haproxy": Functions.INFO,
|
||||
|
|
@ -111,6 +114,7 @@ def test_container_env_stats():
|
|||
"customerrors": False,
|
||||
"ssl_mode": "default",
|
||||
"lookup_label": "easyhaproxy",
|
||||
"host_network_ip": "",
|
||||
"logLevel": {
|
||||
"easyhaproxy": Functions.DEBUG,
|
||||
"haproxy": Functions.INFO,
|
||||
|
|
@ -146,6 +150,7 @@ def test_container_env_stats_password():
|
|||
"customerrors": False,
|
||||
"ssl_mode": "default",
|
||||
"lookup_label": "easyhaproxy",
|
||||
"host_network_ip": "",
|
||||
"stats": {
|
||||
"username": "admin",
|
||||
"password": "xyz",
|
||||
|
|
@ -188,6 +193,7 @@ def test_container_env_stats_password_2():
|
|||
"customerrors": False,
|
||||
"ssl_mode": "default",
|
||||
"lookup_label": "easyhaproxy",
|
||||
"host_network_ip": "",
|
||||
"stats": {
|
||||
"username": "abc",
|
||||
"password": "xyz",
|
||||
|
|
@ -230,6 +236,7 @@ def test_container_env_certbot_email():
|
|||
"customerrors": False,
|
||||
"ssl_mode": "default",
|
||||
"lookup_label": "easyhaproxy",
|
||||
"host_network_ip": "",
|
||||
"logLevel": {
|
||||
"easyhaproxy": Functions.DEBUG,
|
||||
"haproxy": Functions.INFO,
|
||||
|
|
@ -272,6 +279,7 @@ def test_container_env_certbot_full():
|
|||
"customerrors": False,
|
||||
"ssl_mode": "default",
|
||||
"lookup_label": "easyhaproxy",
|
||||
"host_network_ip": "",
|
||||
"logLevel": {
|
||||
"easyhaproxy": Functions.DEBUG,
|
||||
"haproxy": Functions.INFO,
|
||||
|
|
@ -316,6 +324,7 @@ def test_container_log_level():
|
|||
"customerrors": False,
|
||||
"ssl_mode": "default",
|
||||
"lookup_label": "easyhaproxy",
|
||||
"host_network_ip": "",
|
||||
"logLevel": {
|
||||
"easyhaproxy": Functions.ERROR,
|
||||
"haproxy": Functions.FATAL,
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ def test_processor_docker():
|
|||
container2.stop()
|
||||
|
||||
|
||||
def test_processor_docker_ignores_unlabeled():
|
||||
def _skip_unless_docker_is_idle():
|
||||
try:
|
||||
client = docker.from_env()
|
||||
except docker.errors.DockerException:
|
||||
|
|
@ -114,6 +114,230 @@ def test_processor_docker_ignores_unlabeled():
|
|||
if len(client.containers.list()) > 0:
|
||||
pytest.skip("I cannot run this test with other containers running.")
|
||||
|
||||
return client
|
||||
|
||||
|
||||
def _bridge_gateway(client):
|
||||
return client.networks.get("bridge").attrs["IPAM"]["Config"][0]["Gateway"]
|
||||
|
||||
|
||||
def test_processor_docker_host_network():
|
||||
client = _skip_unless_docker_is_idle()
|
||||
|
||||
container = client.containers.run("byjg/static-httpserver",
|
||||
name="test_processor_docker_host",
|
||||
detach=True,
|
||||
auto_remove=True,
|
||||
remove=True,
|
||||
network_mode="host",
|
||||
labels={
|
||||
"easyhaproxy.hostmode.port": "80",
|
||||
"easyhaproxy.hostmode.localport": "8080",
|
||||
"easyhaproxy.hostmode.host": "hostmode.local",
|
||||
})
|
||||
container2 = client.containers.run("byjg/static-httpserver",
|
||||
name="test_processor_docker_bridge",
|
||||
detach=True,
|
||||
auto_remove=True,
|
||||
remove=True,
|
||||
labels={
|
||||
"easyhaproxy.bridged.port": "80",
|
||||
"easyhaproxy.bridged.localport": "8080",
|
||||
"easyhaproxy.bridged.host": "bridged.local",
|
||||
})
|
||||
try:
|
||||
time.sleep(1)
|
||||
|
||||
static = ProcessorInterface.factory(ProcessorInterface.DOCKER)
|
||||
|
||||
assert {
|
||||
'easyhaproxy.hostmode.host': 'hostmode.local',
|
||||
'easyhaproxy.hostmode.localport': '8080',
|
||||
'easyhaproxy.hostmode.port': '80',
|
||||
} == _get_hydrated_object(static.get_parsed_object(), "easyhaproxy.hostmode")
|
||||
|
||||
# The container shares the host network namespace, so it is served through the gateway.
|
||||
assert _get_ip_host(static.get_parsed_object(), "easyhaproxy.hostmode") == _bridge_gateway(client)
|
||||
|
||||
# The container on the bridge network keeps being served through its own address.
|
||||
bridged_ip = client.containers.get(container2.name).attrs["NetworkSettings"]["Networks"]["bridge"]["IPAddress"]
|
||||
assert _get_ip_host(static.get_parsed_object(), "easyhaproxy.bridged") == bridged_ip
|
||||
|
||||
static.get_haproxy_conf()
|
||||
assert static.get_hosts() == [
|
||||
'bridged.local:80',
|
||||
'hostmode.local:80'
|
||||
]
|
||||
finally:
|
||||
container.stop()
|
||||
container2.stop()
|
||||
|
||||
|
||||
def test_processor_docker_host_network_merges_labels():
|
||||
client = _skip_unless_docker_is_idle()
|
||||
|
||||
container = client.containers.run("byjg/static-httpserver",
|
||||
name="test_processor_docker_host1",
|
||||
detach=True,
|
||||
auto_remove=True,
|
||||
remove=True,
|
||||
network_mode="host",
|
||||
labels={
|
||||
"easyhaproxy.first.port": "80",
|
||||
"easyhaproxy.first.localport": "8080",
|
||||
"easyhaproxy.first.host": "first.local",
|
||||
})
|
||||
container2 = client.containers.run("byjg/static-httpserver",
|
||||
name="test_processor_docker_host2",
|
||||
detach=True,
|
||||
auto_remove=True,
|
||||
remove=True,
|
||||
network_mode="host",
|
||||
environment={"PORT": "9000", "TLS_PORT": "9443"},
|
||||
labels={
|
||||
"easyhaproxy.second.port": "80",
|
||||
"easyhaproxy.second.localport": "9000",
|
||||
"easyhaproxy.second.host": "second.local",
|
||||
})
|
||||
container3 = client.containers.run("byjg/static-httpserver",
|
||||
name="test_processor_docker_bridge_merge",
|
||||
detach=True,
|
||||
auto_remove=True,
|
||||
remove=True,
|
||||
labels={
|
||||
"easyhaproxy.bridged.port": "80",
|
||||
"easyhaproxy.bridged.localport": "8080",
|
||||
"easyhaproxy.bridged.host": "bridged.local",
|
||||
})
|
||||
try:
|
||||
time.sleep(1)
|
||||
|
||||
static = ProcessorInterface.factory(ProcessorInterface.DOCKER)
|
||||
gateway = _bridge_gateway(client)
|
||||
|
||||
# Both containers resolve to the same address, so their labels are merged, not replaced.
|
||||
assert _get_ip_host(static.get_parsed_object(), "easyhaproxy.first") == gateway
|
||||
assert _get_ip_host(static.get_parsed_object(), "easyhaproxy.second") == gateway
|
||||
merged = {key: value for key, value in static.get_parsed_object()[gateway].items()
|
||||
if key.startswith("easyhaproxy.")}
|
||||
assert {
|
||||
'easyhaproxy.first.host': 'first.local',
|
||||
'easyhaproxy.first.localport': '8080',
|
||||
'easyhaproxy.first.port': '80',
|
||||
'easyhaproxy.second.host': 'second.local',
|
||||
'easyhaproxy.second.localport': '9000',
|
||||
'easyhaproxy.second.port': '80',
|
||||
} == merged
|
||||
|
||||
haproxy_cfg = static.get_haproxy_conf()
|
||||
assert f"server srv-0 {gateway}:8080" in haproxy_cfg
|
||||
assert f"server srv-0 {gateway}:9000" in haproxy_cfg
|
||||
finally:
|
||||
container.stop()
|
||||
container2.stop()
|
||||
container3.stop()
|
||||
|
||||
|
||||
def test_processor_docker_host_network_ip_override():
|
||||
client = _skip_unless_docker_is_idle()
|
||||
|
||||
container = client.containers.run("byjg/static-httpserver",
|
||||
name="test_processor_docker_host_override",
|
||||
detach=True,
|
||||
auto_remove=True,
|
||||
remove=True,
|
||||
network_mode="host",
|
||||
labels={
|
||||
"easyhaproxy.hostmode.port": "80",
|
||||
"easyhaproxy.hostmode.localport": "8080",
|
||||
"easyhaproxy.hostmode.host": "hostmode.local",
|
||||
})
|
||||
container2 = client.containers.run("byjg/static-httpserver",
|
||||
name="test_processor_docker_bridge_override",
|
||||
detach=True,
|
||||
auto_remove=True,
|
||||
remove=True,
|
||||
labels={
|
||||
"easyhaproxy.bridged.port": "80",
|
||||
"easyhaproxy.bridged.localport": "8080",
|
||||
"easyhaproxy.bridged.host": "bridged.local",
|
||||
})
|
||||
try:
|
||||
time.sleep(1)
|
||||
|
||||
os.environ['EASYHAPROXY_HOST_NETWORK_IP'] = '10.20.30.40'
|
||||
|
||||
static = ProcessorInterface.factory(ProcessorInterface.DOCKER)
|
||||
|
||||
assert _get_ip_host(static.get_parsed_object(), "easyhaproxy.hostmode") == '10.20.30.40'
|
||||
finally:
|
||||
del os.environ['EASYHAPROXY_HOST_NETWORK_IP']
|
||||
container.stop()
|
||||
container2.stop()
|
||||
|
||||
|
||||
def test_processor_docker_shared_namespace():
|
||||
client = _skip_unless_docker_is_idle()
|
||||
|
||||
owner = client.containers.run("byjg/static-httpserver",
|
||||
name="test_processor_docker_owner",
|
||||
detach=True,
|
||||
auto_remove=True,
|
||||
remove=True)
|
||||
time.sleep(1)
|
||||
container = client.containers.run("byjg/static-httpserver",
|
||||
name="test_processor_docker_sidecar",
|
||||
detach=True,
|
||||
auto_remove=True,
|
||||
remove=True,
|
||||
environment={"PORT": "9000", "TLS_PORT": "9443"},
|
||||
network_mode="container:test_processor_docker_owner",
|
||||
labels={
|
||||
"easyhaproxy.sidecar.port": "80",
|
||||
"easyhaproxy.sidecar.localport": "9000",
|
||||
"easyhaproxy.sidecar.host": "sidecar.local",
|
||||
})
|
||||
try:
|
||||
time.sleep(1)
|
||||
|
||||
static = ProcessorInterface.factory(ProcessorInterface.DOCKER)
|
||||
|
||||
# The sidecar is reachable at the address of the container owning the network namespace.
|
||||
owner_ip = client.containers.get(owner.name).attrs["NetworkSettings"]["Networks"]["bridge"]["IPAddress"]
|
||||
assert _get_ip_host(static.get_parsed_object(), "easyhaproxy.sidecar") == owner_ip
|
||||
finally:
|
||||
container.stop()
|
||||
owner.stop()
|
||||
|
||||
|
||||
def test_processor_docker_host_network_only():
|
||||
client = _skip_unless_docker_is_idle()
|
||||
|
||||
container = client.containers.run("byjg/static-httpserver",
|
||||
name="test_processor_docker_host_only",
|
||||
detach=True,
|
||||
auto_remove=True,
|
||||
remove=True,
|
||||
network_mode="host",
|
||||
labels={
|
||||
"easyhaproxy.hostmode.port": "80",
|
||||
"easyhaproxy.hostmode.localport": "8080",
|
||||
"easyhaproxy.hostmode.host": "hostmode.local",
|
||||
})
|
||||
try:
|
||||
time.sleep(1)
|
||||
|
||||
# There is no network to borrow, but the discovery must not fail.
|
||||
static = ProcessorInterface.factory(ProcessorInterface.DOCKER)
|
||||
assert static.get_parsed_object() == {}
|
||||
assert static.get_haproxy_conf() != ""
|
||||
finally:
|
||||
container.stop()
|
||||
|
||||
|
||||
def test_processor_docker_ignores_unlabeled():
|
||||
client = _skip_unless_docker_is_idle()
|
||||
|
||||
network = client.networks.create("test_processor_docker_network", driver="bridge")
|
||||
# The most recent container is inspected first, so the labeled one defines the network to use.
|
||||
unlabeled = client.containers.run("byjg/static-httpserver",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue