From 6162427c4533a7c48881a5e666e206ba9b87084c Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 28 Jan 2023 00:19:21 -0500 Subject: [PATCH] fix: Generate at most one `server` directive per container --- nginx.tmpl | 23 +++++++++++++++++++---- test/test_multiple-networks.py | 19 ++++++++++++++++--- test/test_multiple-networks.yml | 15 +++++++++++++++ 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/nginx.tmpl b/nginx.tmpl index 310b60e..83f9222 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -118,6 +118,7 @@ upstream {{ .Upstream }} { # Container: {{ $container.Name }} {{- /* If only 1 port exposed, use that as a default, else 80 */}} {{- $defaultPort := (when (eq (len $container.Addresses) 1) (first $container.Addresses) (dict "Port" "80")).Port }} + {{- $ip := "" }} {{- $port := (coalesce $container.Env.VIRTUAL_PORT $defaultPort) }} {{- $addr_obj := where $container.Addresses "Port" $port | first }} # Exposed ports:{{ range $container.Addresses }} {{ .Port }}/{{ .Proto }}{{ else }} (none){{ end }} @@ -141,23 +142,37 @@ upstream {{ .Upstream }} { # {{ $containerNetwork.Name }} (unreachable) {{- continue }} {{- end }} + {{- /* + * Do not emit multiple `server` directives for this container + * if it is reachable over multiple networks. This avoids + * accidentally inflating the effective round-robin weight of + * this container due to the redundant upstreams that nginx sees + * as belonging to distinct servers. + */}} + {{- if $ip }} + # {{ $containerNetwork.Name }} (ignored; reachable but redundant) + {{- continue }} + {{- end }} # {{ $containerNetwork.Name }} (reachable) {{- /* * If we got the containers from swarm and this container's * port is published to host, use host IP:PORT. */}} {{- if and $container.Node.ID $addr_obj $addr_obj.HostPort }} - {{- $server_found = true }} - server {{ $container.Node.Address.IP }}:{{ $addr_obj.HostPort }}; + {{- $ip = $container.Node.Address.IP }} + {{- $port = $addr_obj.HostPort }} {{- else if and $containerNetwork $containerNetwork.IP }} - {{- $server_found = true }} - server {{ $containerNetwork.IP }}:{{ $port }}; + {{- $ip = $containerNetwork.IP }} {{- else }} # /!\ No IP for this network! {{- end }} {{- else }} # (none) {{- end }} + {{- if $ip }} + {{- $server_found = true }} + server {{ $ip }}:{{ $port }}; + {{- end }} {{- end }} {{- /* nginx-proxy/nginx-proxy#1105 */}} {{- if not $server_found }} diff --git a/test/test_multiple-networks.py b/test/test_multiple-networks.py index b9fa4c5..550d0a3 100644 --- a/test/test_multiple-networks.py +++ b/test/test_multiple-networks.py @@ -1,15 +1,28 @@ +import re + import pytest + def test_unknown_virtual_host(docker_compose, nginxproxy): r = nginxproxy.get("http://nginx-proxy/") assert r.status_code == 503 def test_forwards_to_web1(docker_compose, nginxproxy): r = nginxproxy.get("http://web1.nginx-proxy.local/port") - assert r.status_code == 200 + assert r.status_code == 200 assert r.text == "answer from port 81\n" def test_forwards_to_web2(docker_compose, nginxproxy): r = nginxproxy.get("http://web2.nginx-proxy.local/port") - assert r.status_code == 200 - assert r.text == "answer from port 82\n" \ No newline at end of file + assert r.status_code == 200 + assert r.text == "answer from port 82\n" + +def test_multipath(docker_compose, nginxproxy): + r = nginxproxy.get("http://web3.nginx-proxy.test/port") + assert r.status_code == 200 + assert r.text == "answer from port 83\n" + cfg = nginxproxy.get_conf().decode() + lines = cfg.splitlines() + web3_server_lines = [l for l in lines + if re.search(r'(?m)^\s*server\s+[^\s]*:83;\s*$', l)] + assert len(web3_server_lines) == 1 diff --git a/test/test_multiple-networks.yml b/test/test_multiple-networks.yml index e4548b5..7e79174 100644 --- a/test/test_multiple-networks.yml +++ b/test/test_multiple-networks.yml @@ -3,6 +3,8 @@ version: '2' networks: net1: {} net2: {} + net3a: {} + net3b: {} services: nginx-proxy: @@ -12,6 +14,8 @@ services: networks: - net1 - net2 + - net3a + - net3b web1: image: web @@ -32,3 +36,14 @@ services: VIRTUAL_HOST: web2.nginx-proxy.local networks: - net2 + + web3: + image: web + expose: + - "83" + environment: + WEB_PORTS: 83 + VIRTUAL_HOST: web3.nginx-proxy.test + networks: + - net3a + - net3b