1
0
mirror of https://github.com/thib8956/nginx-proxy synced 2025-02-24 09:48:14 +00:00

Merge pull request #2434 from nginx-proxy/multiport-support

feat: multiport support
This commit is contained in:
Nicolas Duchon 2024-05-09 12:29:00 +02:00 committed by GitHub
commit db07d90ad8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 566 additions and 111 deletions

View File

@ -54,6 +54,105 @@ For each host defined into `VIRTUAL_HOST`, the associated virtual port is retrie
1. From the container's exposed port if there is only one 1. From the container's exposed port if there is only one
1. From the default port 80 when none of the above methods apply 1. From the default port 80 when none of the above methods apply
### Multiple ports
If your container expose more than one service on different ports and those services need to be proxied, you'll need to use the `VIRTUAL_HOST_MULTIPORTS` environment variable. This variable takes virtual host, path, port and dest definition in YAML (or JSON) form, and completely override the `VIRTUAL_HOST`, `VIRTUAL_PORT`, `VIRTUAL_PATH` and `VIRTUAL_DEST` environment variables on this container.
The YAML syntax should be easier to write on Docker compose files, while the JSON syntax can be used for CLI invocation.
The expected format is the following:
```yaml
hostname:
path:
port: int
dest: string
```
For each hostname entry, `path`, `port` and `dest` are optional and are assigned default values when missing:
- `path` = "/"
- `port` = default port
- `dest` = ""
The following examples use an hypothetical container running services on port 80, 8000 and 9000:
#### Multiple ports routed to different hostnames
```yaml
services:
multiport-container:
image: somerepo/somecontainer
container_name: multiport-container
environment:
VIRTUAL_HOST_MULTIPORTS: |-
www.example.org:
service1.example.org:
"/":
port: 8000
service2.example.org:
"/":
port: 9000
# There is no path dict specified for www.example.org, so it get the default values:
# www.example.org:
# "/":
# port: 80 (default port)
# dest: ""
# JSON equivalent:
# VIRTUAL_HOST_MULTIPORTS: |-
# {
# "www.example.org": {},
# "service1.example.org": { "/": { "port": 8000, "dest": "" } },
# "service2.example.org": { "/": { "port": 9000, "dest": "" } }
# }
```
This would result in the following proxy config:
- `www.example.org` -> `multiport-container:80`
- `service1.example.org` -> `multiport-container:8000`
- `service2.example.org` -> `multiport-container:9000`
#### Multiple ports routed to same hostname and different paths
```yaml
services:
multiport-container:
image: somerepo/somecontainer
container_name: multiport-container
environment:
VIRTUAL_HOST_MULTIPORTS: |-
www.example.org:
"/":
"/service1":
port: 8000
dest: "/"
"/service2":
port: 9000
dest: "/"
# port and dest are not specified on the / path, so this path is routed
# to the default port with the default dest value (empty string)
# JSON equivalent:
# VIRTUAL_HOST_MULTIPORTS: |-
# {
# "www.example.org": {
# "/": {},
# "/service1": { "port": 8000, "dest": "/" },
# "/service2": { "port": 9000, "dest": "/" }
# }
# }
```
This would result in the following proxy config:
- `www.example.org` -> `multiport-container:80`
- `www.example.org/service1` -> `multiport-container:8000`
- `www.example.org/service2` -> `multiport-container:9000`
⬆️ [back to table of contents](#table-of-contents) ⬆️ [back to table of contents](#table-of-contents)
## Path-based Routing ## Path-based Routing

View File

@ -128,7 +128,7 @@
# exposed ports:{{ range sortObjectsByKeysAsc $.container.Addresses "Port" }} {{ .Port }}/{{ .Proto }}{{ else }} (none){{ end }} # exposed ports:{{ range sortObjectsByKeysAsc $.container.Addresses "Port" }} {{ .Port }}/{{ .Proto }}{{ else }} (none){{ end }}
{{- $default_port := when (eq (len $.container.Addresses) 1) (first $.container.Addresses).Port "80" }} {{- $default_port := when (eq (len $.container.Addresses) 1) (first $.container.Addresses).Port "80" }}
# default port: {{ $default_port }} # default port: {{ $default_port }}
{{- $port := when (eq $.port "legacy") (or $.container.Env.VIRTUAL_PORT $default_port) $.port }} {{- $port := when (eq $.port "default") $default_port (when (eq $.port "legacy") (or $.container.Env.VIRTUAL_PORT $default_port) $.port) }}
# using port: {{ $port }} # using port: {{ $port }}
{{- $addr_obj := where $.container.Addresses "Port" $port | first }} {{- $addr_obj := where $.container.Addresses "Port" $port | first }}
{{- if and $addr_obj $addr_obj.HostPort }} {{- if and $addr_obj $addr_obj.HostPort }}
@ -338,49 +338,6 @@ upstream {{ $vpath.upstream }} {
} }
{{- end }} {{- end }}
{{- /*
* Template used as a function to collect virtual path properties from
* the given containers. These properties are "returned" by storing their
* values into the provided dot dict.
*
* The provided dot dict is expected to have the following entries:
* - "Containers": List of container's RuntimeContainer struct.
* - "Upstream_name"
* - "Has_virtual_paths": boolean
* - "Path"
*
* The return values will be added to the dot dict with keys:
* - "dest"
* - "proto"
* - "network_tag"
* - "upstream"
* - "loadbalance"
* - "keepalive"
*/}}
{{- define "get_path_info" }}
{{- /* Get the VIRTUAL_PROTO defined by containers w/ the same vhost-vpath, falling back to "http". */}}
{{- $proto := trim (or (first (groupByKeys $.Containers "Env.VIRTUAL_PROTO")) "http") }}
{{- /* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external". */}}
{{- $network_tag := or (first (groupByKeys $.Containers "Env.NETWORK_ACCESS")) "external" }}
{{- $loadbalance := first (keys (groupByLabel $.Containers "com.github.nginx-proxy.nginx-proxy.loadbalance")) }}
{{- $keepalive := coalesce (first (keys (groupByLabel $.Containers "com.github.nginx-proxy.nginx-proxy.keepalive"))) "disabled" }}
{{- $upstream := $.Upstream_name }}
{{- $dest := "" }}
{{- if $.Has_virtual_paths }}
{{- $sum := sha1 $.Path }}
{{- $upstream = printf "%s-%s" $upstream $sum }}
{{- $dest = or (first (groupByKeys $.Containers "Env.VIRTUAL_DEST")) "" }}
{{- end }}
{{- $_ := set $ "proto" $proto }}
{{- $_ := set $ "network_tag" $network_tag }}
{{- $_ := set $ "upstream" $upstream }}
{{- $_ := set $ "dest" $dest }}
{{- $_ := set $ "loadbalance" $loadbalance }}
{{- $_ := set $ "keepalive" $keepalive }}
{{- end }}
# If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the # If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the
# scheme used to connect to this server # scheme used to connect to this server
map $http_x_forwarded_proto $proxy_x_forwarded_proto { map $http_x_forwarded_proto $proxy_x_forwarded_proto {
@ -500,15 +457,131 @@ proxy_set_header X-Original-URI $request_uri;
proxy_set_header Proxy ""; proxy_set_header Proxy "";
{{- end }} {{- end }}
{{- /* Precompute some information about each vhost. */}} {{- /* Precompute and store some information about vhost that use VIRTUAL_HOST_MULTIPORTS. */}}
{{- range $hostname, $containers := groupByMulti $globals.containers "Env.VIRTUAL_HOST" "," }} {{- range $vhosts_yaml, $containers := groupBy $globals.containers "Env.VIRTUAL_HOST_MULTIPORTS" }}
{{- $hostname = trim $hostname }} {{- /* Print a warning in the config if VIRTUAL_HOST_MULTIPORTS can't be parsed. */}}
{{- if not $hostname }} {{- $parsedVhosts := fromYaml $vhosts_yaml }}
{{- /* Ignore containers with VIRTUAL_HOST set to the empty string. */}} {{- if (empty $parsedVhosts) }}
{{- $containerNames := list }}
{{- range $container := $containers }}
{{- $containerNames = append $containerNames $container.Name }}
{{- end }}
# /!\ WARNING: the VIRTUAL_HOST_MULTIPORTS environment variable used for {{ len $containerNames | plural "this container" "those containers" }} is not a valid YAML string:
# {{ $containerNames | join ", " }}
{{- continue }} {{- continue }}
{{- end }} {{- end }}
{{- $certName := first (groupByKeys $containers "Env.CERT_NAME") }} {{- range $hostname, $vhost := $parsedVhosts }}
{{- $vhost_data := when (hasKey $globals.vhosts $hostname) (get $globals.vhosts $hostname) (dict) }}
{{- $paths := coalesce $vhost_data.paths (dict) }}
{{- if (empty $vhost) }}
{{ $vhost = dict "/" (dict) }}
{{- end }}
{{- range $path, $vpath := $vhost }}
{{- if (empty $vpath) }}
{{- $vpath = dict "dest" "" "port" "default" }}
{{- end }}
{{- $dest := coalesce $vpath.dest "" }}
{{- $port := when (hasKey $vpath "port") (toString $vpath.port) "default" }}
{{- $path_data := when (hasKey $paths $path) (get $paths $path) (dict) }}
{{- $path_ports := when (hasKey $path_data "ports") (get $path_data "ports") (dict) }}
{{- $path_port_containers := when (hasKey $path_ports $port) (get $path_ports $port) (list) }}
{{- $path_port_containers = concat $path_port_containers $containers }}
{{- $_ := set $path_ports $port $path_port_containers }}
{{- $_ := set $path_data "ports" $path_ports }}
{{- if (not (hasKey $path_data "dest")) }}
{{- $_ := set $path_data "dest" $dest }}
{{- end }}
{{- $_ := set $paths $path $path_data }}
{{- end }}
{{- $_ := set $vhost_data "paths" $paths }}
{{- $is_regexp := hasPrefix "~" $hostname }}
{{- $_ := set $vhost_data "upstream_name" (when (or $is_regexp $globals.sha1_upstream_name) (sha1 $hostname) $hostname) }}
{{- $_ := set $globals.vhosts $hostname $vhost_data }}
{{- end }}
{{- end }}
{{- /* Precompute and store some information about vhost that use VIRTUAL_HOST. */}}
{{- range $hostname, $containers := groupByMulti $globals.containers "Env.VIRTUAL_HOST" "," }}
{{- /* Ignore containers with VIRTUAL_HOST set to the empty string. */}}
{{- $hostname = trim $hostname }}
{{- if not $hostname }}
{{- continue }}
{{- end }}
{{- /* Drop containers with both VIRTUAL_HOST and VIRTUAL_HOST_MULTIPORTS set
* (VIRTUAL_HOST_MULTIPORTS takes precedence thanks to the previous loop).
*/}}
{{- range $_, $containers_to_drop := groupBy $containers "Env.VIRTUAL_HOST_MULTIPORTS" }}
{{- range $container := $containers_to_drop }}
{{- $containers = without $containers $container }}
{{- end }}
{{- end }}
{{- if (eq (len $containers) 0) }}
{{- continue }}
{{- end }}
{{- $vhost_data := when (hasKey $globals.vhosts $hostname) (get $globals.vhosts $hostname) (dict) }}
{{- $paths := coalesce $vhost_data.paths (dict) }}
{{- $tmp_paths := groupByWithDefault $containers "Env.VIRTUAL_PATH" "/" }}
{{- range $path, $containers := $tmp_paths }}
{{- $dest := or (first (groupByKeys $containers "Env.VIRTUAL_DEST")) "" }}
{{- $port := "legacy" }}
{{- $path_data := when (hasKey $paths $path) (get $paths $path) (dict) }}
{{- $path_ports := when (hasKey $path_data "ports") (get $path_data "ports") (dict) }}
{{- $path_port_containers := when (hasKey $path_ports $port) (get $path_ports $port) (list) }}
{{- $path_port_containers = concat $path_port_containers $containers }}
{{- $_ := set $path_ports $port $path_port_containers }}
{{- $_ := set $path_data "ports" $path_ports }}
{{- if (not (hasKey $path_data "dest")) }}
{{- $_ := set $path_data "dest" $dest }}
{{- end }}
{{- $_ := set $paths $path $path_data }}
{{- end }}
{{- $_ := set $vhost_data "paths" $paths }}
{{- $is_regexp := hasPrefix "~" $hostname }}
{{- $_ := set $vhost_data "upstream_name" (when (or $is_regexp $globals.sha1_upstream_name) (sha1 $hostname) $hostname) }}
{{- $_ := set $globals.vhosts $hostname $vhost_data }}
{{- end }}
{{- /* Loop over $globals.vhosts and update it with the remaining informations about each vhost. */}}
{{- range $hostname, $vhost_data := $globals.vhosts }}
{{- $vhost_containers := list }}
{{- range $path, $vpath_data := $vhost_data.paths }}
{{- $vpath_containers := list }}
{{- range $port, $vport_containers := $vpath_data.ports }}
{{ $vpath_containers = concat $vpath_containers $vport_containers }}
{{- end }}
{{- /* Get the VIRTUAL_PROTO defined by containers w/ the same vhost-vpath, falling back to "http". */}}
{{- $proto := trim (or (first (groupByKeys $vpath_containers "Env.VIRTUAL_PROTO")) "http") }}
{{- /* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external". */}}
{{- $network_tag := or (first (groupByKeys $vpath_containers "Env.NETWORK_ACCESS")) "external" }}
{{- $loadbalance := first (keys (groupByLabel $vpath_containers "com.github.nginx-proxy.nginx-proxy.loadbalance")) }}
{{- $keepalive := coalesce (first (keys (groupByLabel $vpath_containers "com.github.nginx-proxy.nginx-proxy.keepalive"))) "disabled" }}
{{- $upstream := $vhost_data.upstream_name }}
{{- if (not (eq $path "/")) }}
{{- $sum := sha1 $path }}
{{- $upstream = printf "%s-%s" $upstream $sum }}
{{- end }}
{{- $_ := set $vpath_data "proto" $proto }}
{{- $_ := set $vpath_data "network_tag" $network_tag }}
{{- $_ := set $vpath_data "upstream" $upstream }}
{{- $_ := set $vpath_data "loadbalance" $loadbalance }}
{{- $_ := set $vpath_data "keepalive" $keepalive }}
{{- $_ := set $vhost_data.paths $path $vpath_data }}
{{ $vhost_containers = concat $vhost_containers $vpath_containers }}
{{- end }}
{{- $certName := first (groupByKeys $vhost_containers "Env.CERT_NAME") }}
{{- $vhostCert := closest (dir "/etc/nginx/certs") (printf "%s.crt" $hostname) }} {{- $vhostCert := closest (dir "/etc/nginx/certs") (printf "%s.crt" $hostname) }}
{{- $vhostCert = trimSuffix ".crt" $vhostCert }} {{- $vhostCert = trimSuffix ".crt" $vhostCert }}
{{- $vhostCert = trimSuffix ".key" $vhostCert }} {{- $vhostCert = trimSuffix ".key" $vhostCert }}
@ -516,49 +589,23 @@ proxy_set_header Proxy "";
{{- $cert_ok := and (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert)) }} {{- $cert_ok := and (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert)) }}
{{- $default := eq $globals.Env.DEFAULT_HOST $hostname }} {{- $default := eq $globals.Env.DEFAULT_HOST $hostname }}
{{- $https_method := or (first (groupByKeys $containers "Env.HTTPS_METHOD")) $globals.Env.HTTPS_METHOD "redirect" }} {{- $https_method := or (first (groupByKeys $vhost_containers "Env.HTTPS_METHOD")) $globals.Env.HTTPS_METHOD "redirect" }}
{{- $http2_enabled := parseBool (or (first (keys (groupByLabel $containers "com.github.nginx-proxy.nginx-proxy.http2.enable"))) $globals.Env.ENABLE_HTTP2 "true")}} {{- $http2_enabled := parseBool (or (first (keys (groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.http2.enable"))) $globals.Env.ENABLE_HTTP2 "true")}}
{{- $http3_enabled := parseBool (or (first (keys (groupByLabel $containers "com.github.nginx-proxy.nginx-proxy.http3.enable"))) $globals.Env.ENABLE_HTTP3 "false")}} {{- $http3_enabled := parseBool (or (first (keys (groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.http3.enable"))) $globals.Env.ENABLE_HTTP3 "false")}}
{{- $is_regexp := hasPrefix "~" $hostname }}
{{- $upstream_name := when (or $is_regexp $globals.sha1_upstream_name) (sha1 $hostname) $hostname }}
{{- /* Get the SERVER_TOKENS defined by containers w/ the same vhost, falling back to "". */}} {{- /* Get the SERVER_TOKENS defined by containers w/ the same vhost, falling back to "". */}}
{{- $server_tokens := trim (or (first (groupByKeys $containers "Env.SERVER_TOKENS")) "") }} {{- $server_tokens := trim (or (first (groupByKeys $vhost_containers "Env.SERVER_TOKENS")) "") }}
{{- /* Get the SSL_POLICY defined by containers w/ the same vhost, falling back to empty string (use default). */}} {{- /* Get the SSL_POLICY defined by containers w/ the same vhost, falling back to empty string (use default). */}}
{{- $ssl_policy := or (first (groupByKeys $containers "Env.SSL_POLICY")) "" }} {{- $ssl_policy := or (first (groupByKeys $vhost_containers "Env.SSL_POLICY")) "" }}
{{- /* Get the HSTS defined by containers w/ the same vhost, falling back to "max-age=31536000". */}} {{- /* Get the HSTS defined by containers w/ the same vhost, falling back to "max-age=31536000". */}}
{{- $hsts := or (first (groupByKeys $containers "Env.HSTS")) (or $globals.Env.HSTS "max-age=31536000") }} {{- $hsts := or (first (groupByKeys $vhost_containers "Env.HSTS")) (or $globals.Env.HSTS "max-age=31536000") }}
{{- /* Get the VIRTUAL_ROOT By containers w/ use fastcgi root */}} {{- /* Get the VIRTUAL_ROOT By containers w/ use fastcgi root */}}
{{- $vhost_root := or (first (groupByKeys $containers "Env.VIRTUAL_ROOT")) "/var/www/public" }} {{- $vhost_root := or (first (groupByKeys $vhost_containers "Env.VIRTUAL_ROOT")) "/var/www/public" }}
{{- $vhost_data = merge $vhost_data (dict
{{- $tmp_paths := groupBy $containers "Env.VIRTUAL_PATH" }}
{{- $has_virtual_paths := gt (len $tmp_paths) 0}}
{{- if not $has_virtual_paths }}
{{- $tmp_paths = dict "/" $containers }}
{{- end }}
{{- $paths := dict }}
{{- range $path, $containers := $tmp_paths }}
{{- $args := dict "Containers" $containers "Path" $path "Upstream_name" $upstream_name "Has_virtual_paths" $has_virtual_paths }}
{{- template "get_path_info" $args }}
{{- $_ := set $paths $path (dict
"ports" (dict "legacy" $containers)
"dest" $args.dest
"proto" $args.proto
"network_tag" $args.network_tag
"upstream" $args.upstream
"loadbalance" $args.loadbalance
"keepalive" $args.keepalive
) }}
{{- end }}
{{- $_ := set $globals.vhosts $hostname (dict
"cert" $cert "cert" $cert
"cert_ok" $cert_ok "cert_ok" $cert_ok
"default" $default "default" $default
@ -566,13 +613,14 @@ proxy_set_header Proxy "";
"https_method" $https_method "https_method" $https_method
"http2_enabled" $http2_enabled "http2_enabled" $http2_enabled
"http3_enabled" $http3_enabled "http3_enabled" $http3_enabled
"paths" $paths
"server_tokens" $server_tokens "server_tokens" $server_tokens
"ssl_policy" $ssl_policy "ssl_policy" $ssl_policy
"vhost_root" $vhost_root "vhost_root" $vhost_root
) }} ) }}
{{- $_ := set $globals.vhosts $hostname $vhost_data }}
{{- end }} {{- end }}
{{- /* {{- /*
* If needed, create a catch-all fallback server to send an error code to * If needed, create a catch-all fallback server to send an error code to
* clients that request something from an unknown vhost. * clients that request something from an unknown vhost.

View File

@ -1,10 +0,0 @@
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_whoami(docker_compose, nginxproxy):
r = nginxproxy.get("http://web.nginx-proxy.example/port")
assert r.status_code == 200
assert r.text == "answer from port 81\n"

View File

@ -1,15 +0,0 @@
version: "2"
services:
nginx-proxy:
image: nginxproxy/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
web:
image: web
expose:
- "81"
environment:
WEB_PORTS: 81
VIRTUAL_HOST: web.nginx-proxy.example

View File

@ -0,0 +1,39 @@
import pytest
def test_virtual_host_is_dropped_when_using_multiports(docker_compose, nginxproxy):
r = nginxproxy.get("http://notskipped.nginx-proxy.tld/port")
assert r.status_code == 200
assert "answer from port 81\n" in r.text
r = nginxproxy.get("http://skipped.nginx-proxy.tld/")
assert r.status_code == 503
def test_answer_is_served_from_port_80_by_default(docker_compose, nginxproxy):
r = nginxproxy.get("http://port80.a.nginx-proxy.tld/port")
assert r.status_code == 200
assert "answer from port 80\n" in r.text
r = nginxproxy.get("http://port80.b.nginx-proxy.tld/port")
assert r.status_code == 200
assert "answer from port 80\n" in r.text
r = nginxproxy.get("http://port80.c.nginx-proxy.tld/port")
assert r.status_code == 200
assert "answer from port 80\n" in r.text
def test_answer_is_served_from_chosen_ports(docker_compose, nginxproxy):
r = nginxproxy.get("http://port8080.nginx-proxy.tld/port")
assert r.status_code == 200
assert "answer from port 8080\n" in r.text
r = nginxproxy.get("http://port9000.nginx-proxy.tld/port")
assert r.status_code == 200
assert "answer from port 9000\n" in r.text
def test_answer_is_served_from_chosen_ports_and_dest(docker_compose, nginxproxy):
r = nginxproxy.get("http://virtualpaths.nginx-proxy.tld/rootdest/port")
assert r.status_code == 200
assert "answer from port 10001\n" in r.text
r = nginxproxy.get("http://virtualpaths.nginx-proxy.tld/customdest")
assert r.status_code == 200
assert "answer from port 10002\n" in r.text

View File

@ -0,0 +1,77 @@
version: "2"
services:
skipvirtualhost:
image: web
expose:
- "81"
environment:
WEB_PORTS: "81"
VIRTUAL_HOST: skipped.nginx-proxy.tld
VIRTUAL_HOST_MULTIPORTS: |-
{
"notskipped.nginx-proxy.tld": {}
}
defaultport:
image: web
expose:
- "80"
- "8080"
environment:
WEB_PORTS: "80 8080"
VIRTUAL_HOST_MULTIPORTS: |-
{
"port80.a.nginx-proxy.tld": {},
"port80.b.nginx-proxy.tld": {},
"port80.c.nginx-proxy.tld": {
"/": {}
}
}
multiports:
image: web
expose:
- "8080"
- "9000"
environment:
WEB_PORTS: "8080 9000"
VIRTUAL_HOST_MULTIPORTS: |-
{
"port8080.nginx-proxy.tld": {
"/": {
"port": 8080
}
},
"port9000.nginx-proxy.tld": {
"/": {
"port": 9000
}
}
}
virtualpath:
image: web
expose:
- "10001"
- "10002"
environment:
WEB_PORTS: "10001 10002"
VIRTUAL_HOST_MULTIPORTS: |-
{
"virtualpaths.nginx-proxy.tld": {
"/rootdest": {
"port": 10001,
"dest": "/"
},
"/customdest": {
"port": 10002,
"dest": "/port"
}
}
}
sut:
image: nginxproxy/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro

View File

@ -0,0 +1,39 @@
import pytest
def test_virtual_host_is_dropped_when_using_multiports(docker_compose, nginxproxy):
r = nginxproxy.get("http://notskipped.nginx-proxy.tld/port")
assert r.status_code == 200
assert "answer from port 81\n" in r.text
r = nginxproxy.get("http://skipped.nginx-proxy.tld/")
assert r.status_code == 503
def test_answer_is_served_from_port_80_by_default(docker_compose, nginxproxy):
r = nginxproxy.get("http://port80.a.nginx-proxy.tld/port")
assert r.status_code == 200
assert "answer from port 80\n" in r.text
r = nginxproxy.get("http://port80.b.nginx-proxy.tld/port")
assert r.status_code == 200
assert "answer from port 80\n" in r.text
r = nginxproxy.get("http://port80.c.nginx-proxy.tld/port")
assert r.status_code == 200
assert "answer from port 80\n" in r.text
def test_answer_is_served_from_chosen_ports(docker_compose, nginxproxy):
r = nginxproxy.get("http://port8080.nginx-proxy.tld/port")
assert r.status_code == 200
assert "answer from port 8080\n" in r.text
r = nginxproxy.get("http://port9000.nginx-proxy.tld/port")
assert r.status_code == 200
assert "answer from port 9000\n" in r.text
def test_answer_is_served_from_chosen_ports_and_dest(docker_compose, nginxproxy):
r = nginxproxy.get("http://virtualpaths.nginx-proxy.tld/rootdest/port")
assert r.status_code == 200
assert "answer from port 10001\n" in r.text
r = nginxproxy.get("http://virtualpaths.nginx-proxy.tld/customdest")
assert r.status_code == 200
assert "answer from port 10002\n" in r.text

View File

@ -0,0 +1,61 @@
version: "2"
services:
skipvirtualhost:
image: web
expose:
- "81"
environment:
WEB_PORTS: "81"
VIRTUAL_HOST: skipped.nginx-proxy.tld
VIRTUAL_HOST_MULTIPORTS: |-
notskipped.nginx-proxy.tld:
defaultport:
image: web
expose:
- "80"
- "8080"
environment:
WEB_PORTS: "80 8080"
VIRTUAL_HOST_MULTIPORTS: |-
port80.a.nginx-proxy.tld:
port80.b.nginx-proxy.tld:
port80.c.nginx-proxy.tld:
"/":
multiports:
image: web
expose:
- "8080"
- "9000"
environment:
WEB_PORTS: "8080 9000"
VIRTUAL_HOST_MULTIPORTS: |-
port8080.nginx-proxy.tld:
"/":
port: 8080
port9000.nginx-proxy.tld:
"/":
port: 9000
virtualpath:
image: web
expose:
- "10001"
- "10002"
environment:
WEB_PORTS: "10001 10002"
VIRTUAL_HOST_MULTIPORTS: |-
virtualpaths.nginx-proxy.tld:
"/rootdest":
port: 10001
dest: "/"
"/customdest":
port: 10002
dest: "/port"
sut:
image: nginxproxy/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro

View File

@ -0,0 +1,18 @@
import pytest
import re
def test_virtual_hosts_with_syntax_error_should_not_be_reachable(docker_compose, nginxproxy):
r = nginxproxy.get("http://test1.nginx-proxy.tld")
assert r.status_code == 503
r = nginxproxy.get("http://test2.nginx-proxy.tld")
assert r.status_code == 503
def test_config_should_have_multiports_warning_comments(docker_compose, nginxproxy):
conf = nginxproxy.get_conf().decode('ASCII')
matches = re.findall(r"the VIRTUAL_HOST_MULTIPORTS environment variable used for this container is not a valid YAML string", conf)
assert len(matches) == 3
assert "# invalidsyntax" in conf
assert "# hostnamerepeat" in conf
assert "# pathrepeat" in conf

View File

@ -0,0 +1,44 @@
version: "2"
services:
invalidsyntax:
image: web
container_name: invalidsyntax
expose:
- "80"
environment:
WEB_PORTS: "80"
VIRTUAL_HOST_MULTIPORTS: |-
test1.nginx-proxy.tld
test2.nginx-proxy.tld:
hostnamerepeat:
image: web
container_name: hostnamerepeat
expose:
- "80"
environment:
WEB_PORTS: "80"
VIRTUAL_HOST_MULTIPORTS: |-
test1.nginx-proxy.tld:
test1.nginx-proxy.tld:
pathrepeat:
image: web
container_name: pathrepeat
expose:
- "8080"
- "9000"
environment:
WEB_PORTS: "8080 9000"
VIRTUAL_HOST_MULTIPORTS: |-
test1.nginx-proxy.tld:
"/":
port: 8080
"/":
port: 9000
sut:
image: nginxproxy/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro

View File

@ -0,0 +1,14 @@
import backoff
import pytest
def test_multiports_and_legacy_configs_should_be_merged(docker_compose, nginxproxy):
@backoff.on_predicate(backoff.constant, lambda r: r == False, interval=.5, max_tries=20, jitter=None)
def answer_contains(answer, url):
return answer in nginxproxy.get(url).text
assert answer_contains("80", "http://merged.nginx-proxy.tld/port")
assert answer_contains("81", "http://merged.nginx-proxy.tld/port")
assert answer_contains("9090", "http://merged.nginx-proxy.tld/foo/port")
assert answer_contains("9191", "http://merged.nginx-proxy.tld/foo/port")

View File

@ -0,0 +1,41 @@
version: "2"
services:
merged-singleport:
image: web
expose:
- "80"
environment:
WEB_PORTS: "80"
VIRTUAL_HOST: merged.nginx-proxy.tld
merged-singleport-virtual-path:
image: web
expose:
- "9090"
environment:
WEB_PORTS: "9090"
VIRTUAL_HOST: merged.nginx-proxy.tld
VIRTUAL_PORT: "9090"
VIRTUAL_PATH: "/foo"
VIRTUAL_DEST: "/"
merged-multiports:
image: web
expose:
- "81"
- "9191"
environment:
WEB_PORTS: "81 9191"
VIRTUAL_HOST_MULTIPORTS: |-
merged.nginx-proxy.tld:
"/":
port: 81
"/foo":
port: 9191
dest: "/"
sut:
image: nginxproxy/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro