Compare commits
23 commits
main
...
feat/docke
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a919c93455 | ||
![]() |
eb97a413c0 | ||
![]() |
41d0e33966 | ||
![]() |
381293ad3f | ||
![]() |
8fd179bf9f | ||
![]() |
2c595e09c9 | ||
![]() |
dab99aa3e3 | ||
![]() |
1c5a527cfc | ||
![]() |
8493fc7aea | ||
![]() |
ce9e534ad0 | ||
![]() |
77978eab96 | ||
![]() |
c6987b3ea0 | ||
![]() |
5ef2e31687 | ||
![]() |
7acf4780d5 | ||
![]() |
9fd66de39d | ||
![]() |
209192e778 | ||
![]() |
1430f9fbff | ||
![]() |
19200326a3 | ||
![]() |
d3154bb8bc | ||
![]() |
b6f0556b70 | ||
![]() |
e0bbd59313 | ||
![]() |
e736f84b87 | ||
![]() |
a67aff92e9 |
277 changed files with 4242 additions and 2042 deletions
21
.github/workflows/build-publish.yml
vendored
21
.github/workflows/build-publish.yml
vendored
|
@ -23,7 +23,7 @@ jobs:
|
||||||
name: Build and publish image
|
name: Build and publish image
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
base: [alpine, debian]
|
flavor: [alpine, debian, dockergen]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
@ -37,7 +37,7 @@ jobs:
|
||||||
|
|
||||||
- name: Retrieve docker-gen version
|
- name: Retrieve docker-gen version
|
||||||
id: docker-gen_version
|
id: docker-gen_version
|
||||||
run: sed -n -e 's;^FROM nginxproxy/docker-gen:\([0-9.]*\).*;VERSION=\1;p' Dockerfile.${{ matrix.base }} >> "$GITHUB_OUTPUT"
|
run: sed -n -e 's;^FROM docker.io/nginxproxy/docker-gen:\([0-9.]*\).*;VERSION=\1;p' Dockerfile.${{ matrix.flavor }} >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Get Docker tags
|
- name: Get Docker tags
|
||||||
id: docker_meta
|
id: docker_meta
|
||||||
|
@ -48,12 +48,15 @@ jobs:
|
||||||
nginxproxy/nginx-proxy
|
nginxproxy/nginx-proxy
|
||||||
jwilder/nginx-proxy
|
jwilder/nginx-proxy
|
||||||
tags: |
|
tags: |
|
||||||
type=semver,pattern={{version}},enable=${{ matrix.base == 'debian' }}
|
type=semver,pattern={{version}},enable=${{ matrix.flavor == 'debian' }}
|
||||||
type=semver,pattern={{major}}.{{minor}},enable=${{ matrix.base == 'debian' }}
|
type=semver,pattern={{major}}.{{minor}},enable=${{ matrix.flavor == 'debian' }}
|
||||||
type=semver,suffix=-alpine,pattern={{version}},enable=${{ matrix.base == 'alpine' }}
|
type=semver,suffix=-alpine,pattern={{version}},enable=${{ matrix.flavor == 'alpine' }}
|
||||||
type=semver,suffix=-alpine,pattern={{major}}.{{minor}},enable=${{ matrix.base == 'alpine' }}
|
type=semver,suffix=-alpine,pattern={{major}}.{{minor}},enable=${{ matrix.flavor == 'alpine' }}
|
||||||
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' && matrix.base == 'debian' }}
|
type=semver,suffix=-dockergen,pattern={{version}},enable=${{ matrix.flavor == 'dockergen' }}
|
||||||
type=raw,value=alpine,enable=${{ github.ref == 'refs/heads/main' && matrix.base == 'alpine' }}
|
type=semver,suffix=-dockergen,pattern={{major}}.{{minor}},enable=${{ matrix.flavor == 'dockergen' }}
|
||||||
|
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' && matrix.flavor == 'debian' }}
|
||||||
|
type=raw,value=alpine,enable=${{ github.ref == 'refs/heads/main' && matrix.flavor == 'alpine' }}
|
||||||
|
type=raw,value=dockergen,enable=${{ github.ref == 'refs/heads/main' && matrix.flavor == 'dockergen' }}
|
||||||
labels: |
|
labels: |
|
||||||
org.opencontainers.image.authors=Nicolas Duchon <nicolas.duchon@gmail.com> (@buchdag), Jason Wilder
|
org.opencontainers.image.authors=Nicolas Duchon <nicolas.duchon@gmail.com> (@buchdag), Jason Wilder
|
||||||
org.opencontainers.image.version=${{ steps.nginx-proxy_version.outputs.VERSION }}
|
org.opencontainers.image.version=${{ steps.nginx-proxy_version.outputs.VERSION }}
|
||||||
|
@ -84,7 +87,7 @@ jobs:
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: Dockerfile.${{ matrix.base }}
|
file: Dockerfile.${{ matrix.flavor }}
|
||||||
build-args: |
|
build-args: |
|
||||||
NGINX_PROXY_VERSION=${{ steps.nginx-proxy_version.outputs.VERSION }}
|
NGINX_PROXY_VERSION=${{ steps.nginx-proxy_version.outputs.VERSION }}
|
||||||
DOCKER_GEN_VERSION=${{ steps.docker-gen_version.outputs.VERSION }}
|
DOCKER_GEN_VERSION=${{ steps.docker-gen_version.outputs.VERSION }}
|
||||||
|
|
6
.github/workflows/test.yml
vendored
6
.github/workflows/test.yml
vendored
|
@ -20,7 +20,7 @@ jobs:
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
base_docker_image: [alpine, debian]
|
flavor: [alpine, debian, dockergen]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
@ -43,8 +43,10 @@ jobs:
|
||||||
run: make build-webserver
|
run: make build-webserver
|
||||||
|
|
||||||
- name: Build Docker nginx proxy test image
|
- name: Build Docker nginx proxy test image
|
||||||
run: make build-nginx-proxy-test-${{ matrix.base_docker_image }}
|
run: make build-nginx-proxy-test-${{ matrix.flavor }}
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: pytest
|
run: pytest
|
||||||
working-directory: test
|
working-directory: test
|
||||||
|
env:
|
||||||
|
COMPOSE_PROFILES: ${{ matrix.flavor == 'dockergen' && 'separateContainers' || 'singleContainer' }}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM docker.io/nginxproxy/docker-gen:0.14.5 AS docker-gen
|
FROM docker.io/nginxproxy/docker-gen:0.14.4 AS docker-gen
|
||||||
|
|
||||||
FROM docker.io/nginxproxy/forego:0.18.2 AS forego
|
FROM docker.io/nginxproxy/forego:0.18.2 AS forego
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM docker.io/nginxproxy/docker-gen:0.14.5-debian AS docker-gen
|
FROM docker.io/nginxproxy/docker-gen:0.14.4-debian AS docker-gen
|
||||||
|
|
||||||
FROM docker.io/nginxproxy/forego:0.18.2-debian AS forego
|
FROM docker.io/nginxproxy/forego:0.18.2-debian AS forego
|
||||||
|
|
||||||
|
|
18
Dockerfile.dockergen
Normal file
18
Dockerfile.dockergen
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
FROM docker.io/nginxproxy/docker-gen:0.14.4
|
||||||
|
|
||||||
|
ARG NGINX_PROXY_VERSION
|
||||||
|
ENV NGINX_PROXY_VERSION=${NGINX_PROXY_VERSION} \
|
||||||
|
DOCKER_HOST=unix:///tmp/docker.sock \
|
||||||
|
DHPARAM_SKIP=true \
|
||||||
|
NGINX_CONTAINER_NAME=nginx-proxy
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN apk add --no-cache --virtual .run-deps bash
|
||||||
|
|
||||||
|
RUN mkdir -p '/etc/nginx/conf.d'
|
||||||
|
|
||||||
|
COPY app nginx.tmpl LICENSE /app/
|
||||||
|
WORKDIR /app/
|
||||||
|
|
||||||
|
ENTRYPOINT ["/app/docker-entrypoint.sh"]
|
||||||
|
CMD ["docker-gen", "-notify-sighup", "$NGINX_CONTAINER_NAME", "-watch", "/app/nginx.tmpl", "/etc/nginx/conf.d/default.conf"]
|
11
Makefile
11
Makefile
|
@ -11,10 +11,19 @@ build-nginx-proxy-test-debian:
|
||||||
build-nginx-proxy-test-alpine:
|
build-nginx-proxy-test-alpine:
|
||||||
docker build --pull --build-arg NGINX_PROXY_VERSION="test" -f Dockerfile.alpine -t nginxproxy/nginx-proxy:test .
|
docker build --pull --build-arg NGINX_PROXY_VERSION="test" -f Dockerfile.alpine -t nginxproxy/nginx-proxy:test .
|
||||||
|
|
||||||
|
build-nginx-proxy-test-dockergen:
|
||||||
|
docker build --pull --build-arg NGINX_PROXY_VERSION="test" -f Dockerfile.dockergen -t nginxproxy/nginx-proxy:test-dockergen .
|
||||||
|
|
||||||
|
test-debian: export COMPOSE_PROFILES = singleContainer
|
||||||
test-debian: build-webserver build-nginx-proxy-test-debian
|
test-debian: build-webserver build-nginx-proxy-test-debian
|
||||||
test/pytest.sh
|
test/pytest.sh
|
||||||
|
|
||||||
|
test-alpine: export COMPOSE_PROFILES = singleContainer
|
||||||
test-alpine: build-webserver build-nginx-proxy-test-alpine
|
test-alpine: build-webserver build-nginx-proxy-test-alpine
|
||||||
test/pytest.sh
|
test/pytest.sh
|
||||||
|
|
||||||
test: test-debian test-alpine
|
test-dockergen: export COMPOSE_PROFILES = separateContainers
|
||||||
|
test-dockergen: build-webserver build-nginx-proxy-test-docker-gen
|
||||||
|
test/pytest.sh
|
||||||
|
|
||||||
|
test: test-debian test-alpine test-dockergen
|
||||||
|
|
24
README.md
24
README.md
|
@ -20,17 +20,7 @@ docker run --detach \
|
||||||
--volume /var/run/docker.sock:/tmp/docker.sock:ro \
|
--volume /var/run/docker.sock:/tmp/docker.sock:ro \
|
||||||
nginxproxy/nginx-proxy:1.6
|
nginxproxy/nginx-proxy:1.6
|
||||||
```
|
```
|
||||||
docker-compose
|
|
||||||
```docker-compose
|
|
||||||
services:
|
|
||||||
nginx-proxy:
|
|
||||||
image: nginxproxy/nginx-proxy
|
|
||||||
restart: always
|
|
||||||
ports:
|
|
||||||
- "80:80"
|
|
||||||
volumes:
|
|
||||||
- "/var/run/docker.sock:/tmp/docker.sock"
|
|
||||||
```
|
|
||||||
Then start any containers (here an nginx container) you want proxied with an env var `VIRTUAL_HOST=subdomain.yourdomain.com`
|
Then start any containers (here an nginx container) you want proxied with an env var `VIRTUAL_HOST=subdomain.yourdomain.com`
|
||||||
|
|
||||||
```console
|
```console
|
||||||
|
@ -39,12 +29,7 @@ docker run --detach \
|
||||||
--env VIRTUAL_HOST=foo.bar.com \
|
--env VIRTUAL_HOST=foo.bar.com \
|
||||||
nginx
|
nginx
|
||||||
```
|
```
|
||||||
docker-compose
|
|
||||||
```docker-compose
|
|
||||||
environment:
|
|
||||||
- VIRTUAL_HOST=git.patachina.casacam.net
|
|
||||||
- VIRTUAL_PORT=3000
|
|
||||||
```
|
|
||||||
Provided your DNS is setup to resolve `foo.bar.com` to the host running nginx-proxy, a request to `http://foo.bar.com` will then be routed to a container with the `VIRTUAL_HOST` env var set to `foo.bar.com` (in this case, the **your-proxied-app** container).
|
Provided your DNS is setup to resolve `foo.bar.com` to the host running nginx-proxy, a request to `http://foo.bar.com` will then be routed to a container with the `VIRTUAL_HOST` env var set to `foo.bar.com` (in this case, the **your-proxied-app** container).
|
||||||
|
|
||||||
The containers being proxied must :
|
The containers being proxied must :
|
||||||
|
@ -85,8 +70,3 @@ docker pull nginxproxy/nginx-proxy:1.6-alpine
|
||||||
### Additional documentation
|
### Additional documentation
|
||||||
|
|
||||||
Please check the [docs section](https://github.com/nginx-proxy/nginx-proxy/tree/main/docs).
|
Please check the [docs section](https://github.com/nginx-proxy/nginx-proxy/tree/main/docs).
|
||||||
|
|
||||||
### Powered by
|
|
||||||
|
|
||||||
[](https://www.jetbrains.com/go/)
|
|
||||||
[](https://www.jetbrains.com/pycharm/)
|
|
||||||
|
|
|
@ -101,7 +101,7 @@ function _setup_dhparam() {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Run the init logic if the default CMD was provided
|
# Run the init logic if the default CMD was provided
|
||||||
if [[ $* == 'forego start -r' ]]; then
|
if [[ $* == "forego start -r" ]] || [[ $* =~ "docker-gen -notify-sighup" ]]; then
|
||||||
_print_version
|
_print_version
|
||||||
|
|
||||||
_check_unix_socket
|
_check_unix_socket
|
||||||
|
@ -116,6 +116,11 @@ if [[ $* == 'forego start -r' ]]; then
|
||||||
Warning: The default value of TRUST_DOWNSTREAM_PROXY might change to "false" in a future version of nginx-proxy. If you require TRUST_DOWNSTREAM_PROXY to be enabled, explicitly set it to "true".
|
Warning: The default value of TRUST_DOWNSTREAM_PROXY might change to "false" in a future version of nginx-proxy. If you require TRUST_DOWNSTREAM_PROXY to be enabled, explicitly set it to "true".
|
||||||
EOT
|
EOT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ $3 == "\$NGINX_CONTAINER_NAME" && -n "$NGINX_CONTAINER_NAME" ]]; then
|
||||||
|
# change the value of $3 to the expanded $NGINX_CONTAINER_NAME variable
|
||||||
|
set -- "${@:1:2}" "$NGINX_CONTAINER_NAME" "${@:4}"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exec "$@"
|
exec "$@"
|
||||||
|
|
|
@ -56,7 +56,7 @@ For each host defined into `VIRTUAL_HOST`, the associated virtual port is retrie
|
||||||
|
|
||||||
### Multiple ports
|
### 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_PROTO`, `VIRTUAL_PATH` and `VIRTUAL_DEST` environment variables on this container.
|
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 YAML syntax should be easier to write on Docker compose files, while the JSON syntax can be used for CLI invocation.
|
||||||
|
|
||||||
|
@ -66,20 +66,18 @@ The expected format is the following:
|
||||||
hostname:
|
hostname:
|
||||||
path:
|
path:
|
||||||
port: int
|
port: int
|
||||||
proto: string
|
|
||||||
dest: string
|
dest: string
|
||||||
```
|
```
|
||||||
|
|
||||||
For each hostname entry, `path`, `port`, `proto` and `dest` are optional and are assigned default values when missing:
|
For each hostname entry, `path`, `port` and `dest` are optional and are assigned default values when missing:
|
||||||
|
|
||||||
- `path` = "/"
|
- `path` = "/"
|
||||||
- `port` = default port
|
- `port` = default port
|
||||||
- `proto` = "http"
|
|
||||||
- `dest` = ""
|
- `dest` = ""
|
||||||
|
|
||||||
#### Multiple ports routed to different hostnames
|
The following examples use an hypothetical container running services on port 80, 8000 and 9000:
|
||||||
|
|
||||||
The following example use an hypothetical container running services over HTTP on port 80, 8000 and 9000:
|
#### Multiple ports routed to different hostnames
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
services:
|
services:
|
||||||
|
@ -113,14 +111,12 @@ services:
|
||||||
|
|
||||||
This would result in the following proxy config:
|
This would result in the following proxy config:
|
||||||
|
|
||||||
- `www.example.org` -> `multiport-container:80` over `HTTP`
|
- `www.example.org` -> `multiport-container:80`
|
||||||
- `service1.example.org` -> `multiport-container:8000` over `HTTP`
|
- `service1.example.org` -> `multiport-container:8000`
|
||||||
- `service2.example.org` -> `multiport-container:9000` over `HTTP`
|
- `service2.example.org` -> `multiport-container:9000`
|
||||||
|
|
||||||
#### Multiple ports routed to same hostname and different paths
|
#### Multiple ports routed to same hostname and different paths
|
||||||
|
|
||||||
The following example use an hypothetical container running services over HTTP on port 80 and 8000 and over HTTPS on port 9443:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
services:
|
services:
|
||||||
multiport-container:
|
multiport-container:
|
||||||
|
@ -134,12 +130,11 @@ services:
|
||||||
port: 8000
|
port: 8000
|
||||||
dest: "/"
|
dest: "/"
|
||||||
"/service2":
|
"/service2":
|
||||||
port: 9443
|
port: 9000
|
||||||
proto: "https"
|
|
||||||
dest: "/"
|
dest: "/"
|
||||||
|
|
||||||
# port and dest are not specified on the / path, so this path is routed to the
|
# port and dest are not specified on the / path, so this path is routed
|
||||||
# default port with the default dest value (empty string) and default proto (http)
|
# to the default port with the default dest value (empty string)
|
||||||
|
|
||||||
# JSON equivalent:
|
# JSON equivalent:
|
||||||
# VIRTUAL_HOST_MULTIPORTS: |-
|
# VIRTUAL_HOST_MULTIPORTS: |-
|
||||||
|
@ -147,16 +142,16 @@ services:
|
||||||
# "www.example.org": {
|
# "www.example.org": {
|
||||||
# "/": {},
|
# "/": {},
|
||||||
# "/service1": { "port": 8000, "dest": "/" },
|
# "/service1": { "port": 8000, "dest": "/" },
|
||||||
# "/service2": { "port": 9443, "proto": "https", "dest": "/" }
|
# "/service2": { "port": 9000, "dest": "/" }
|
||||||
# }
|
# }
|
||||||
# }
|
# }
|
||||||
```
|
```
|
||||||
|
|
||||||
This would result in the following proxy config:
|
This would result in the following proxy config:
|
||||||
|
|
||||||
- `www.example.org` -> `multiport-container:80` over `HTTP`
|
- `www.example.org` -> `multiport-container:80`
|
||||||
- `www.example.org/service1` -> `multiport-container:8000` over `HTTP`
|
- `www.example.org/service1` -> `multiport-container:8000`
|
||||||
- `www.example.org/service2` -> `multiport-container:9443` over `HTTPS`
|
- `www.example.org/service2` -> `multiport-container:9000`
|
||||||
|
|
||||||
⬆️ [back to table of contents](#table-of-contents)
|
⬆️ [back to table of contents](#table-of-contents)
|
||||||
|
|
||||||
|
@ -580,8 +575,6 @@ The default behavior for the proxy when port 80 and 443 are exposed is as follow
|
||||||
- If the virtual host does not have a usable cert, but `default.crt` and `default.key` exist, those will be used as the virtual host's certificate.
|
- If the virtual host does not have a usable cert, but `default.crt` and `default.key` exist, those will be used as the virtual host's certificate.
|
||||||
- If the virtual host does not have a usable cert, and `default.crt` and `default.key` do not exist, or if the virtual host is configured not to trust the default certificate, SSL handshake will be rejected (see [Default and Missing Certificate](#default-and-missing-certificate) below).
|
- If the virtual host does not have a usable cert, and `default.crt` and `default.key` do not exist, or if the virtual host is configured not to trust the default certificate, SSL handshake will be rejected (see [Default and Missing Certificate](#default-and-missing-certificate) below).
|
||||||
|
|
||||||
The redirection from HTTP to HTTPS use by default a [`301`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/301) response for every HTTP methods (except `CONNECT` and `TRACE` which are disabled on nginx). If you wish to use a custom redirection response for the `OPTIONS`, `POST`, `PUT`, `PATCH` and `DELETE` HTTP methods, you can either do it globally with the environment variable `NON_GET_REDIRECT` on the proxy container or per virtual host with the `com.github.nginx-proxy.nginx-proxy.non-get-redirect` label on proxied containers. Valid values are [`307`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307) and [`308`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308).
|
|
||||||
|
|
||||||
To serve traffic in both SSL and non-SSL modes without redirecting to SSL, you can include the environment variable `HTTPS_METHOD=noredirect` (the default is `HTTPS_METHOD=redirect`). You can also disable the non-SSL site entirely with `HTTPS_METHOD=nohttp`, or disable the HTTPS site with `HTTPS_METHOD=nohttps`. `HTTPS_METHOD` can be specified on each container for which you want to override the default behavior or on the proxy container to set it globally. If `HTTPS_METHOD=noredirect` is used, Strict Transport Security (HSTS) is disabled to prevent HTTPS users from being redirected by the client. If you cannot get to the HTTP site after changing this setting, your browser has probably cached the HSTS policy and is automatically redirecting you back to HTTPS. You will need to clear your browser's HSTS cache or use an incognito window / different browser.
|
To serve traffic in both SSL and non-SSL modes without redirecting to SSL, you can include the environment variable `HTTPS_METHOD=noredirect` (the default is `HTTPS_METHOD=redirect`). You can also disable the non-SSL site entirely with `HTTPS_METHOD=nohttp`, or disable the HTTPS site with `HTTPS_METHOD=nohttps`. `HTTPS_METHOD` can be specified on each container for which you want to override the default behavior or on the proxy container to set it globally. If `HTTPS_METHOD=noredirect` is used, Strict Transport Security (HSTS) is disabled to prevent HTTPS users from being redirected by the client. If you cannot get to the HTTP site after changing this setting, your browser has probably cached the HSTS policy and is automatically redirecting you back to HTTPS. You will need to clear your browser's HSTS cache or use an incognito window / different browser.
|
||||||
|
|
||||||
By default, [HTTP Strict Transport Security (HSTS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) is enabled with `max-age=31536000` for HTTPS sites. You can disable HSTS with the environment variable `HSTS=off` or use a custom HSTS configuration like `HSTS=max-age=31536000; includeSubDomains; preload`.
|
By default, [HTTP Strict Transport Security (HSTS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) is enabled with `max-age=31536000` for HTTPS sites. You can disable HSTS with the environment variable `HSTS=off` or use a custom HSTS configuration like `HSTS=max-age=31536000; includeSubDomains; preload`.
|
||||||
|
|
42
nginx.tmpl
42
nginx.tmpl
|
@ -32,7 +32,6 @@
|
||||||
{{- $_ := set $config "enable_http3" ($globals.Env.ENABLE_HTTP3 | default "false") }}
|
{{- $_ := set $config "enable_http3" ($globals.Env.ENABLE_HTTP3 | default "false") }}
|
||||||
{{- $_ := set $config "enable_http_on_missing_cert" ($globals.Env.ENABLE_HTTP_ON_MISSING_CERT | default "true") }}
|
{{- $_ := set $config "enable_http_on_missing_cert" ($globals.Env.ENABLE_HTTP_ON_MISSING_CERT | default "true") }}
|
||||||
{{- $_ := set $config "https_method" ($globals.Env.HTTPS_METHOD | default "redirect") }}
|
{{- $_ := set $config "https_method" ($globals.Env.HTTPS_METHOD | default "redirect") }}
|
||||||
{{- $_ := set $config "non_get_redirect" ($globals.Env.NON_GET_REDIRECT | default "301") }}
|
|
||||||
{{- $_ := set $config "default_host" $globals.Env.DEFAULT_HOST }}
|
{{- $_ := set $config "default_host" $globals.Env.DEFAULT_HOST }}
|
||||||
{{- $_ := set $config "resolvers" $globals.Env.RESOLVERS }}
|
{{- $_ := set $config "resolvers" $globals.Env.RESOLVERS }}
|
||||||
{{- /* LOG_JSON is a shorthand that sets logging defaults to JSON format */}}
|
{{- /* LOG_JSON is a shorthand that sets logging defaults to JSON format */}}
|
||||||
|
@ -590,31 +589,18 @@ proxy_set_header Proxy "";
|
||||||
|
|
||||||
{{- range $path, $vpath := $vhost }}
|
{{- range $path, $vpath := $vhost }}
|
||||||
{{- if (empty $vpath) }}
|
{{- if (empty $vpath) }}
|
||||||
{{- $vpath = dict
|
{{- $vpath = dict "dest" "" "port" "default" }}
|
||||||
"dest" ""
|
|
||||||
"port" "default"
|
|
||||||
"proto" "http"
|
|
||||||
}}
|
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
{{- $dest := $vpath.dest | default "" }}
|
{{- $dest := $vpath.dest | default "" }}
|
||||||
{{- $port := $vpath.port | default "default" | toString }}
|
{{- $port := $vpath.port | default "default" | toString }}
|
||||||
{{- $proto := $vpath.proto | default "http" }}
|
|
||||||
|
|
||||||
{{- $path_data := get $paths $path | default (dict) }}
|
{{- $path_data := get $paths $path | default (dict) }}
|
||||||
{{- $path_ports := $path_data.ports | default (dict) }}
|
{{- $path_ports := $path_data.ports | default (dict) }}
|
||||||
{{- $path_port_containers := get $path_ports $port | default (list) | concat $containers }}
|
{{- $path_port_containers := get $path_ports $port | default (list) | concat $containers }}
|
||||||
{{- $_ := set $path_ports $port $path_port_containers }}
|
{{- $_ := set $path_ports $port $path_port_containers }}
|
||||||
{{- $_ := set $path_data "ports" $path_ports }}
|
{{- $_ := set $path_data "ports" $path_ports }}
|
||||||
|
|
||||||
{{- if (not (hasKey $path_data "dest")) }}
|
{{- if (not (hasKey $path_data "dest")) }}
|
||||||
{{- $_ := set $path_data "dest" $dest }}
|
{{- $_ := set $path_data "dest" $dest }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
{{- if (not (hasKey $path_data "proto")) }}
|
|
||||||
{{- $_ := set $path_data "proto" $proto }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{- $_ := set $paths $path $path_data }}
|
{{- $_ := set $paths $path $path_data }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- $_ := set $vhost_data "paths" $paths }}
|
{{- $_ := set $vhost_data "paths" $paths }}
|
||||||
|
@ -649,8 +635,6 @@ proxy_set_header Proxy "";
|
||||||
|
|
||||||
{{- range $path, $containers := $tmp_paths }}
|
{{- range $path, $containers := $tmp_paths }}
|
||||||
{{- $dest := groupByKeys $containers "Env.VIRTUAL_DEST" | first | default "" }}
|
{{- $dest := groupByKeys $containers "Env.VIRTUAL_DEST" | first | default "" }}
|
||||||
{{- $proto := groupByKeys $containers "Env.VIRTUAL_PROTO" | first | default "http" | trim }}
|
|
||||||
|
|
||||||
{{- $path_data := get $paths $path | default (dict) }}
|
{{- $path_data := get $paths $path | default (dict) }}
|
||||||
{{- $path_ports := $path_data.ports | default (dict) }}
|
{{- $path_ports := $path_data.ports | default (dict) }}
|
||||||
{{- range $port, $containers := groupByWithDefault $containers "Env.VIRTUAL_PORT" "default" }}
|
{{- range $port, $containers := groupByWithDefault $containers "Env.VIRTUAL_PORT" "default" }}
|
||||||
|
@ -658,15 +642,9 @@ proxy_set_header Proxy "";
|
||||||
{{- $_ := set $path_ports $port $path_port_containers }}
|
{{- $_ := set $path_ports $port $path_port_containers }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- $_ := set $path_data "ports" $path_ports }}
|
{{- $_ := set $path_data "ports" $path_ports }}
|
||||||
|
|
||||||
{{- if (not (hasKey $path_data "dest")) }}
|
{{- if (not (hasKey $path_data "dest")) }}
|
||||||
{{- $_ := set $path_data "dest" $dest }}
|
{{- $_ := set $path_data "dest" $dest }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
{{- if (not (hasKey $path_data "proto")) }}
|
|
||||||
{{- $_ := set $path_data "proto" $proto }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{- $_ := set $paths $path $path_data }}
|
{{- $_ := set $paths $path $path_data }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- $_ := set $vhost_data "paths" $paths }}
|
{{- $_ := set $vhost_data "paths" $paths }}
|
||||||
|
@ -686,6 +664,8 @@ proxy_set_header Proxy "";
|
||||||
{{ $vpath_containers = concat $vpath_containers $vport_containers }}
|
{{ $vpath_containers = concat $vpath_containers $vport_containers }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
|
{{- /* Get the VIRTUAL_PROTO defined by containers w/ the same vhost-vpath, falling back to "http". */}}
|
||||||
|
{{- $proto := groupByKeys $vpath_containers "Env.VIRTUAL_PROTO" | first | default "http" | trim }}
|
||||||
{{- /* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external". */}}
|
{{- /* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external". */}}
|
||||||
{{- $network_tag := groupByKeys $vpath_containers "Env.NETWORK_ACCESS" | first | default "external" }}
|
{{- $network_tag := groupByKeys $vpath_containers "Env.NETWORK_ACCESS" | first | default "external" }}
|
||||||
|
|
||||||
|
@ -698,6 +678,7 @@ proxy_set_header Proxy "";
|
||||||
{{- $upstream = printf "%s-%s" $upstream $sum }}
|
{{- $upstream = printf "%s-%s" $upstream $sum }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
|
{{- $_ := set $vpath_data "proto" $proto }}
|
||||||
{{- $_ := set $vpath_data "network_tag" $network_tag }}
|
{{- $_ := set $vpath_data "network_tag" $network_tag }}
|
||||||
{{- $_ := set $vpath_data "upstream" $upstream }}
|
{{- $_ := set $vpath_data "upstream" $upstream }}
|
||||||
{{- $_ := set $vpath_data "loadbalance" $loadbalance }}
|
{{- $_ := set $vpath_data "loadbalance" $loadbalance }}
|
||||||
|
@ -737,11 +718,8 @@ proxy_set_header Proxy "";
|
||||||
{{- if and $https_method_disable_http (not $cert_ok) $enable_http_on_missing_cert }}
|
{{- if and $https_method_disable_http (not $cert_ok) $enable_http_on_missing_cert }}
|
||||||
{{- $https_method = "noredirect" }}
|
{{- $https_method = "noredirect" }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- $non_get_redirect := groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.non-get-redirect" | keys | first | default $globals.config.non_get_redirect }}
|
|
||||||
|
|
||||||
{{- $http2_enabled := groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.http2.enable" | keys | first | default $globals.config.enable_http2 | parseBool }}
|
{{- $http2_enabled := groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.http2.enable" | keys | first | default $globals.config.enable_http2 | parseBool }}
|
||||||
{{- $http3_enabled := groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.http3.enable" | keys | first | default $globals.config.enable_http3 | parseBool }}
|
{{- $http3_enabled := groupByLabel $vhost_containers "com.github.nginx-proxy.nginx-proxy.http3.enable" | keys | first | default $globals.config.enable_http3 | parseBool }}
|
||||||
|
|
||||||
{{- $acme_http_challenge := groupByKeys $vhost_containers "Env.ACME_HTTP_CHALLENGE_LOCATION" | first | default $globals.config.acme_http_challenge }}
|
{{- $acme_http_challenge := groupByKeys $vhost_containers "Env.ACME_HTTP_CHALLENGE_LOCATION" | first | default $globals.config.acme_http_challenge }}
|
||||||
{{- $acme_http_challenge_legacy := eq $acme_http_challenge "legacy" }}
|
{{- $acme_http_challenge_legacy := eq $acme_http_challenge "legacy" }}
|
||||||
{{- $acme_http_challenge_enabled := false }}
|
{{- $acme_http_challenge_enabled := false }}
|
||||||
|
@ -768,7 +746,6 @@ proxy_set_header Proxy "";
|
||||||
"default" $default
|
"default" $default
|
||||||
"hsts" $hsts
|
"hsts" $hsts
|
||||||
"https_method" $https_method
|
"https_method" $https_method
|
||||||
"non_get_redirect" $non_get_redirect
|
|
||||||
"http2_enabled" $http2_enabled
|
"http2_enabled" $http2_enabled
|
||||||
"http3_enabled" $http3_enabled
|
"http3_enabled" $http3_enabled
|
||||||
"is_regexp" $is_regexp
|
"is_regexp" $is_regexp
|
||||||
|
@ -909,14 +886,11 @@ server {
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
{{- $redirect_uri := "https://$host$request_uri" }}
|
{{- if eq $globals.config.external_https_port "443" }}
|
||||||
{{- if ne $globals.config.external_https_port "443" }}
|
return 301 https://$host$request_uri;
|
||||||
{{- $redirect_uri = printf "https://$host:%s$request_uri" $globals.config.external_https_port }}
|
{{- else }}
|
||||||
|
return 301 https://$host:{{ $globals.config.external_https_port }}$request_uri;
|
||||||
{{- end }}
|
{{- end }}
|
||||||
if ($request_method ~ (OPTIONS|POST|PUT|PATCH|DELETE)) {
|
|
||||||
return {{ $vhost.non_get_redirect }} {{ $redirect_uri }};
|
|
||||||
}
|
|
||||||
return 301 {{ $redirect_uri }};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
|
@ -57,39 +57,13 @@ This test suite uses [pytest](http://doc.pytest.org/en/latest/). The [conftest.p
|
||||||
|
|
||||||
### docker_compose fixture
|
### docker_compose fixture
|
||||||
|
|
||||||
When using the `docker_compose` fixture in a test, pytest will try to start the [Docker Compose](https://docs.docker.com/compose/) services corresponding to the current test module, based on the test module filename.
|
When using the `docker_compose` fixture in a test, pytest will try to find a yml file named after your test module filename. For instance, if your test module is `test_example.py`, then the `docker_compose` fixture will try to load a `test_example.yml` [docker compose file](https://docs.docker.com/compose/compose-file/).
|
||||||
|
|
||||||
By default, if your test module file is `test/test_subdir/test_example.py`, then the `docker_compose` fixture will try to load the following files, [merging them](https://docs.docker.com/reference/compose-file/merge/) in this order:
|
Once the docker compose file found, the fixture will remove all containers, run `docker compose up`, and finally your test will be executed.
|
||||||
|
|
||||||
1. `test/compose.base.yml`
|
The fixture will run the _docker compose_ command with the `-f` option to load the given compose file. So you can test your docker compose file syntax by running it yourself with:
|
||||||
2. `test/test_subdir/compose.base.override.yml` (if it exists)
|
|
||||||
3. `test/test_subdir/test_example.yml`
|
|
||||||
|
|
||||||
The fixture will run the _docker compose_ command with the `-f` option to load the given compose files. So you can test your docker compose file syntax by running it yourself with:
|
docker compose -f test_example.yml up -d
|
||||||
|
|
||||||
docker compose -f test/compose.base.yml -f test/test_subdir/test_example.yml up -d
|
|
||||||
|
|
||||||
The first file contains the base configuration of the nginx-proxy container common to most tests:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
services:
|
|
||||||
nginx-proxy:
|
|
||||||
image: nginxproxy/nginx-proxy:test
|
|
||||||
container_name: nginx-proxy
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
|
||||||
ports:
|
|
||||||
- "80:80"
|
|
||||||
- "443:443"
|
|
||||||
```
|
|
||||||
|
|
||||||
The second optional file allow you to override this base configuration for all test modules in a subfolder.
|
|
||||||
|
|
||||||
The third file contains the services and overrides specific to a given test module.
|
|
||||||
|
|
||||||
This automatic merge can be bypassed by using a file named `test_example.base.yml` (instead of `test_example.yml`). When this file exist, it will be the only one used by the test and no merge with other compose files will automatically occur.
|
|
||||||
|
|
||||||
The `docker_compose` fixture also set the `PYTEST_MODULE_PATH` environment variable to the absolute path of the current test module directory, so it can be used to mount files or directory relatives to the current test.
|
|
||||||
|
|
||||||
In the case you are running pytest from within a docker container, the `docker_compose` fixture will make sure the container running pytest is attached to all docker networks. That way, your test will be able to reach any of them.
|
In the case you are running pytest from within a docker container, the `docker_compose` fixture will make sure the container running pytest is attached to all docker networks. That way, your test will be able to reach any of them.
|
||||||
|
|
||||||
|
@ -97,10 +71,7 @@ In your tests, you can use the `docker_compose` variable to query and command th
|
||||||
|
|
||||||
Also this fixture alters the way the python interpreter resolves domain names to IP addresses in the following ways:
|
Also this fixture alters the way the python interpreter resolves domain names to IP addresses in the following ways:
|
||||||
|
|
||||||
Any domain name containing the substring `nginx-proxy` will resolve to `127.0.0.1` if the tests are executed on a Darwin (macOS) system, otherwise the IP address of the container that was created from the `nginxproxy/nginx-proxy:test` image.
|
Any domain name containing the substring `nginx-proxy` will resolve to the IP address of the container that was created from the `nginxproxy/nginx-proxy:test` image. So all the following domain names will resolve to the nginx-proxy container in tests:
|
||||||
|
|
||||||
So, in tests, all the following domain names will resolve to either localhost or the nginx-proxy container's IP:
|
|
||||||
|
|
||||||
- `nginx-proxy`
|
- `nginx-proxy`
|
||||||
- `nginx-proxy.com`
|
- `nginx-proxy.com`
|
||||||
- `www.nginx-proxy.com`
|
- `www.nginx-proxy.com`
|
||||||
|
@ -109,16 +80,14 @@ So, in tests, all the following domain names will resolve to either localhost or
|
||||||
- `whatever.nginx-proxyooooooo`
|
- `whatever.nginx-proxyooooooo`
|
||||||
- ...
|
- ...
|
||||||
|
|
||||||
Any domain name ending with `XXX.container.docker` will resolve to `127.0.0.1` if the tests are executed on a Darwin (macOS) system, otherwise the IP address of the container named `XXX`.
|
Any domain name ending with `XXX.container.docker` will resolve to the IP address of the XXX container.
|
||||||
|
|
||||||
So, on a non-Darwin system:
|
|
||||||
|
|
||||||
- `web1.container.docker` will resolve to the IP address of the `web1` container
|
- `web1.container.docker` will resolve to the IP address of the `web1` container
|
||||||
- `f00.web1.container.docker` will resolve to the IP address of the `web1` container
|
- `f00.web1.container.docker` will resolve to the IP address of the `web1` container
|
||||||
- `anything.whatever.web2.container.docker` will resolve to the IP address of the `web2` container
|
- `anything.whatever.web2.container.docker` will resolve to the IP address of the `web2` container
|
||||||
|
|
||||||
Otherwise, domain names are resoved as usual using your system DNS resolver.
|
Otherwise, domain names are resoved as usual using your system DNS resolver.
|
||||||
|
|
||||||
|
|
||||||
### nginxproxy fixture
|
### nginxproxy fixture
|
||||||
|
|
||||||
The `nginxproxy` fixture will provide you with a replacement for the python [requests](https://pypi.python.org/pypi/requests/) module. This replacement will just repeat up to 30 times a requests if it receives the HTTP error 404 or 502. This error occurs when you try to send queries to nginx-proxy too early after the container creation.
|
The `nginxproxy` fixture will provide you with a replacement for the python [requests](https://pypi.python.org/pypi/requests/) module. This replacement will just repeat up to 30 times a requests if it receives the HTTP error 404 or 502. This error occurs when you try to send queries to nginx-proxy too early after the container creation.
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
services:
|
|
||||||
nginx-proxy:
|
|
||||||
image: nginxproxy/nginx-proxy:test
|
|
||||||
container_name: nginx-proxy
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
|
||||||
ports:
|
|
||||||
- "80:80"
|
|
||||||
- "443:443"
|
|
397
test/conftest.py
397
test/conftest.py
|
@ -1,39 +1,35 @@
|
||||||
import contextlib
|
import contextlib
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import pathlib
|
|
||||||
import platform
|
import platform
|
||||||
import re
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
import socket
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
from io import StringIO
|
from typing import List
|
||||||
from typing import Iterator, List, Optional
|
|
||||||
|
|
||||||
import backoff
|
import backoff
|
||||||
import docker.errors
|
import docker.errors
|
||||||
import pytest
|
import pytest
|
||||||
import requests
|
import requests
|
||||||
from _pytest.fixtures import FixtureRequest
|
|
||||||
from docker import DockerClient
|
|
||||||
from docker.models.containers import Container
|
from docker.models.containers import Container
|
||||||
from docker.models.networks import Network
|
|
||||||
from packaging.version import Version
|
from packaging.version import Version
|
||||||
from requests import Response
|
from requests.packages.urllib3.util.connection import HAS_IPV6
|
||||||
from urllib3.util.connection import HAS_IPV6
|
|
||||||
|
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
logging.getLogger('backoff').setLevel(logging.INFO)
|
logging.getLogger('backoff').setLevel(logging.INFO)
|
||||||
logging.getLogger('DNS').setLevel(logging.DEBUG)
|
logging.getLogger('DNS').setLevel(logging.DEBUG)
|
||||||
logging.getLogger('requests.packages.urllib3.connectionpool').setLevel(logging.WARN)
|
logging.getLogger('requests.packages.urllib3.connectionpool').setLevel(logging.WARN)
|
||||||
|
|
||||||
CA_ROOT_CERTIFICATE = pathlib.Path(__file__).parent.joinpath("certs/ca-root.crt")
|
CA_ROOT_CERTIFICATE = os.path.join(os.path.dirname(__file__), 'certs/ca-root.crt')
|
||||||
PYTEST_RUNNING_IN_CONTAINER = os.environ.get('PYTEST_RUNNING_IN_CONTAINER') == "1"
|
PYTEST_RUNNING_IN_CONTAINER = os.environ.get('PYTEST_RUNNING_IN_CONTAINER') == "1"
|
||||||
FORCE_CONTAINER_IPV6 = False # ugly global state to consider containers' IPv6 address instead of IPv4
|
FORCE_CONTAINER_IPV6 = False # ugly global state to consider containers' IPv6 address instead of IPv4
|
||||||
|
|
||||||
DOCKER_COMPOSE = os.environ.get('DOCKER_COMPOSE', 'docker compose')
|
DOCKER_COMPOSE = os.environ.get('DOCKER_COMPOSE', 'docker compose')
|
||||||
|
COMPOSE_PROFILES = os.environ.get('COMPOSE_PROFILES', 'singleContainer')
|
||||||
|
IMAGE_TAG = "test-dockergen" if COMPOSE_PROFILES == "separateContainers" else "test"
|
||||||
|
|
||||||
|
|
||||||
docker_client = docker.from_env()
|
docker_client = docker.from_env()
|
||||||
|
|
||||||
|
@ -47,9 +43,8 @@ test_container = 'nginx-proxy-pytest'
|
||||||
#
|
#
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def ipv6(force_ipv6: bool = True):
|
def ipv6(force_ipv6=True):
|
||||||
"""
|
"""
|
||||||
Meant to be used as a context manager to force IPv6 sockets:
|
Meant to be used as a context manager to force IPv6 sockets:
|
||||||
|
|
||||||
|
@ -67,90 +62,119 @@ def ipv6(force_ipv6: bool = True):
|
||||||
FORCE_CONTAINER_IPV6 = False
|
FORCE_CONTAINER_IPV6 = False
|
||||||
|
|
||||||
|
|
||||||
class RequestsForDocker:
|
class requests_for_docker(object):
|
||||||
"""
|
"""
|
||||||
Proxy for calling methods of the requests module.
|
Proxy for calling methods of the requests module.
|
||||||
When an HTTP response failed due to HTTP Error 404 or 502, retry a few times.
|
When a HTTP response failed due to HTTP Error 404 or 502, retry a few times.
|
||||||
Provides method `get_conf` to extract the nginx-proxy configuration content.
|
Provides method `get_conf` to extract the nginx-proxy configuration content.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.session = requests.Session()
|
self.session = requests.Session()
|
||||||
if CA_ROOT_CERTIFICATE.is_file():
|
if os.path.isfile(CA_ROOT_CERTIFICATE):
|
||||||
self.session.verify = CA_ROOT_CERTIFICATE.as_posix()
|
self.session.verify = CA_ROOT_CERTIFICATE
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_nginx_proxy_container() -> Container:
|
def __backoff_predicate(expected_status_codes=None):
|
||||||
|
if expected_status_codes is not None:
|
||||||
|
if isinstance(expected_status_codes, int):
|
||||||
|
expected_status_codes = [expected_status_codes]
|
||||||
|
return lambda r: r.status_code not in expected_status_codes
|
||||||
|
else:
|
||||||
|
return lambda r: r.status_code not in (200, 301)
|
||||||
|
|
||||||
|
__backed_off_exceptions = (requests.exceptions.SSLError, requests.exceptions.ConnectionError)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_nginx_proxy_containers() -> List[Container]:
|
||||||
"""
|
"""
|
||||||
Return list of containers
|
Return list of containers
|
||||||
"""
|
"""
|
||||||
nginx_proxy_containers = docker_client.containers.list(filters={"ancestor": "nginxproxy/nginx-proxy:test"})
|
nginx_proxy_containers = docker_client.containers.list(filters={"ancestor": f"nginxproxy/nginx-proxy:{IMAGE_TAG}"})
|
||||||
if len(nginx_proxy_containers) > 1:
|
if len(nginx_proxy_containers) > 1:
|
||||||
pytest.fail("Too many running nginxproxy/nginx-proxy:test containers", pytrace=False)
|
pytest.fail(f"Too many running nginxproxy/nginx-proxy:{IMAGE_TAG} containers", pytrace=False)
|
||||||
elif len(nginx_proxy_containers) == 0:
|
elif len(nginx_proxy_containers) == 0:
|
||||||
pytest.fail("No running nginxproxy/nginx-proxy:test container", pytrace=False)
|
pytest.fail(f"No running nginxproxy/nginx-proxy:{IMAGE_TAG} container", pytrace=False)
|
||||||
return nginx_proxy_containers.pop()
|
return nginx_proxy_containers
|
||||||
|
|
||||||
def get_conf(self) -> bytes:
|
def get_conf(self):
|
||||||
"""
|
"""
|
||||||
Return the nginx config file
|
Return the nginx config file
|
||||||
"""
|
"""
|
||||||
nginx_proxy_container = self.get_nginx_proxy_container()
|
nginx_proxy_containers = self.get_nginx_proxy_containers()
|
||||||
return get_nginx_conf_from_container(nginx_proxy_container)
|
return get_nginx_conf_from_container(nginx_proxy_containers[0])
|
||||||
|
|
||||||
def get_ip(self) -> str:
|
def get_ip(self) -> str:
|
||||||
"""
|
"""
|
||||||
Return the nginx container ip address
|
Return the nginx container ip address
|
||||||
"""
|
"""
|
||||||
nginx_proxy_container = self.get_nginx_proxy_container()
|
nginx_proxy_containers = self.get_nginx_proxy_containers()
|
||||||
return container_ip(nginx_proxy_container)
|
return container_ip(nginx_proxy_containers[0])
|
||||||
|
|
||||||
def get(self, *args, **kwargs) -> Response:
|
def get(self, *args, **kwargs):
|
||||||
|
_expected_status_code = kwargs.pop('expected_status_code', None)
|
||||||
with ipv6(kwargs.pop('ipv6', False)):
|
with ipv6(kwargs.pop('ipv6', False)):
|
||||||
@backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None)
|
@backoff.on_exception(backoff.constant, self.__backed_off_exceptions, interval=.3, max_tries=30, jitter=None)
|
||||||
def _get(*_args, **_kwargs):
|
@backoff.on_predicate(backoff.constant, self.__backoff_predicate(_expected_status_code), interval=.3, max_tries=30, jitter=None)
|
||||||
return self.session.get(*_args, **_kwargs)
|
def _get(*args, **kwargs):
|
||||||
|
return self.session.get(*args, **kwargs)
|
||||||
return _get(*args, **kwargs)
|
return _get(*args, **kwargs)
|
||||||
|
|
||||||
def post(self, *args, **kwargs) -> Response:
|
def get_without_backoff(self, *args, **kwargs):
|
||||||
with ipv6(kwargs.pop('ipv6', False)):
|
with ipv6(kwargs.pop('ipv6', False)):
|
||||||
@backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None)
|
def _get(*args, **kwargs):
|
||||||
def _post(*_args, **_kwargs):
|
return self.session.get(*args, **kwargs)
|
||||||
return self.session.post(*_args, **_kwargs)
|
return _get(*args, **kwargs)
|
||||||
|
|
||||||
|
def post(self, *args, **kwargs):
|
||||||
|
_expected_status_code = kwargs.pop('expected_status_code', None)
|
||||||
|
with ipv6(kwargs.pop('ipv6', False)):
|
||||||
|
@backoff.on_exception(backoff.constant, requests.exceptions.SSLError, interval=.3, max_tries=30, jitter=None)
|
||||||
|
@backoff.on_predicate(backoff.constant, self.__backoff_predicate(_expected_status_code), interval=.3, max_tries=30, jitter=None)
|
||||||
|
def _post(*args, **kwargs):
|
||||||
|
return self.session.post(*args, **kwargs)
|
||||||
return _post(*args, **kwargs)
|
return _post(*args, **kwargs)
|
||||||
|
|
||||||
def put(self, *args, **kwargs) -> Response:
|
def put(self, *args, **kwargs):
|
||||||
|
_expected_status_code = kwargs.pop('expected_status_code', None)
|
||||||
with ipv6(kwargs.pop('ipv6', False)):
|
with ipv6(kwargs.pop('ipv6', False)):
|
||||||
@backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None)
|
@backoff.on_exception(backoff.constant, requests.exceptions.SSLError, interval=.3, max_tries=30, jitter=None)
|
||||||
def _put(*_args, **_kwargs):
|
@backoff.on_predicate(backoff.constant, self.__backoff_predicate(_expected_status_code), interval=.3, max_tries=30, jitter=None)
|
||||||
return self.session.put(*_args, **_kwargs)
|
def _put(*args, **kwargs):
|
||||||
|
return self.session.put(*args, **kwargs)
|
||||||
return _put(*args, **kwargs)
|
return _put(*args, **kwargs)
|
||||||
|
|
||||||
def head(self, *args, **kwargs) -> Response:
|
def head(self, *args, **kwargs):
|
||||||
|
_expected_status_code = kwargs.pop('expected_status_code', None)
|
||||||
with ipv6(kwargs.pop('ipv6', False)):
|
with ipv6(kwargs.pop('ipv6', False)):
|
||||||
@backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None)
|
@backoff.on_exception(backoff.constant, requests.exceptions.SSLError, interval=.3, max_tries=30, jitter=None)
|
||||||
def _head(*_args, **_kwargs):
|
@backoff.on_predicate(backoff.constant, self.__backoff_predicate(_expected_status_code), interval=.3, max_tries=30, jitter=None)
|
||||||
return self.session.head(*_args, **_kwargs)
|
def _head(*args, **kwargs):
|
||||||
|
return self.session.head(*args, **kwargs)
|
||||||
return _head(*args, **kwargs)
|
return _head(*args, **kwargs)
|
||||||
|
|
||||||
def delete(self, *args, **kwargs) -> Response:
|
def delete(self, *args, **kwargs):
|
||||||
|
_expected_status_code = kwargs.pop('expected_status_code', None)
|
||||||
with ipv6(kwargs.pop('ipv6', False)):
|
with ipv6(kwargs.pop('ipv6', False)):
|
||||||
@backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None)
|
@backoff.on_exception(backoff.constant, requests.exceptions.SSLError, interval=.3, max_tries=30, jitter=None)
|
||||||
def _delete(*_args, **_kwargs):
|
@backoff.on_predicate(backoff.constant, self.__backoff_predicate(_expected_status_code), interval=.3, max_tries=30, jitter=None)
|
||||||
return self.session.delete(*_args, **_kwargs)
|
def _delete(*args, **kwargs):
|
||||||
|
return self.session.delete(*args, **kwargs)
|
||||||
return _delete(*args, **kwargs)
|
return _delete(*args, **kwargs)
|
||||||
|
|
||||||
def options(self, *args, **kwargs) -> Response:
|
def options(self, *args, **kwargs):
|
||||||
|
_expected_status_code = kwargs.pop('expected_status_code', None)
|
||||||
with ipv6(kwargs.pop('ipv6', False)):
|
with ipv6(kwargs.pop('ipv6', False)):
|
||||||
@backoff.on_predicate(backoff.constant, lambda r: r.status_code in (404, 502), interval=.3, max_tries=30, jitter=None)
|
@backoff.on_exception(backoff.constant, requests.exceptions.SSLError, interval=.3, max_tries=30, jitter=None)
|
||||||
def _options(*_args, **_kwargs):
|
@backoff.on_predicate(backoff.constant, self.__backoff_predicate(_expected_status_code), interval=.3, max_tries=30, jitter=None)
|
||||||
return self.session.options(*_args, **_kwargs)
|
def _options(*args, **kwargs):
|
||||||
|
return self.session.options(*args, **kwargs)
|
||||||
return _options(*args, **kwargs)
|
return _options(*args, **kwargs)
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
return getattr(requests, name)
|
return getattr(requests, name)
|
||||||
|
|
||||||
|
|
||||||
def container_ip(container: Container) -> str:
|
def container_ip(container: Container):
|
||||||
"""
|
"""
|
||||||
return the IP address of a container.
|
return the IP address of a container.
|
||||||
|
|
||||||
|
@ -179,7 +203,7 @@ def container_ip(container: Container) -> str:
|
||||||
return net_info[network_name]["IPAddress"]
|
return net_info[network_name]["IPAddress"]
|
||||||
|
|
||||||
|
|
||||||
def container_ipv6(container: Container) -> str:
|
def container_ipv6(container):
|
||||||
"""
|
"""
|
||||||
return the IPv6 address of a container.
|
return the IPv6 address of a container.
|
||||||
"""
|
"""
|
||||||
|
@ -196,7 +220,7 @@ def container_ipv6(container: Container) -> str:
|
||||||
return net_info[network_name]["GlobalIPv6Address"]
|
return net_info[network_name]["GlobalIPv6Address"]
|
||||||
|
|
||||||
|
|
||||||
def nginx_proxy_dns_resolver(domain_name: str) -> Optional[str]:
|
def nginx_proxy_single_container_dns_resolver(domain_name):
|
||||||
"""
|
"""
|
||||||
if "nginx-proxy" if found in host, return the ip address of the docker container
|
if "nginx-proxy" if found in host, return the ip address of the docker container
|
||||||
issued from the docker image nginxproxy/nginx-proxy:test.
|
issued from the docker image nginxproxy/nginx-proxy:test.
|
||||||
|
@ -204,25 +228,48 @@ def nginx_proxy_dns_resolver(domain_name: str) -> Optional[str]:
|
||||||
:return: IP or None
|
:return: IP or None
|
||||||
"""
|
"""
|
||||||
log = logging.getLogger('DNS')
|
log = logging.getLogger('DNS')
|
||||||
log.debug(f"nginx_proxy_dns_resolver({domain_name!r})")
|
log.debug(f"nginx_proxy_single_container_dns_resolver({domain_name!r})")
|
||||||
if 'nginx-proxy' in domain_name:
|
if 'nginx-proxy' in domain_name:
|
||||||
nginxproxy_containers = docker_client.containers.list(filters={"status": "running", "ancestor": "nginxproxy/nginx-proxy:test"})
|
nginxproxy_containers = docker_client.containers.list(filters={"status": "running", "ancestor": "nginxproxy/nginx-proxy:test"})
|
||||||
if len(nginxproxy_containers) == 0:
|
if len(nginxproxy_containers) == 0:
|
||||||
log.warning(f"no container found from image nginxproxy/nginx-proxy:test while resolving {domain_name!r}")
|
log.info(f"no container found from image nginxproxy/nginx-proxy:test while resolving {domain_name!r}")
|
||||||
exited_nginxproxy_containers = docker_client.containers.list(filters={"status": "exited", "ancestor": "nginxproxy/nginx-proxy:test"})
|
exited_nginxproxy_containers = docker_client.containers.list(filters={"status": "exited", "ancestor": "nginxproxy/nginx-proxy:test"})
|
||||||
if len(exited_nginxproxy_containers) > 0:
|
if len(exited_nginxproxy_containers) > 0:
|
||||||
exited_nginxproxy_container_logs = exited_nginxproxy_containers[0].logs()
|
exited_nginxproxy_container_logs = exited_nginxproxy_containers[0].logs()
|
||||||
log.warning(f"nginxproxy/nginx-proxy:test container might have exited unexpectedly. Container logs: " + "\n" + exited_nginxproxy_container_logs.decode())
|
log.warning(f"nginxproxy/nginx-proxy:test container might have exited unexpectedly. Container logs: " + "\n" + exited_nginxproxy_container_logs.decode())
|
||||||
return None
|
return
|
||||||
nginxproxy_container = nginxproxy_containers[0]
|
nginxproxy_container = nginxproxy_containers[0]
|
||||||
ip = container_ip(nginxproxy_container)
|
ip = container_ip(nginxproxy_container)
|
||||||
log.info(f"resolving domain name {domain_name!r} as IP address {ip} of nginx-proxy container {nginxproxy_container.name}")
|
log.info(f"resolving domain name {domain_name!r} as IP address {ip} of nginx-proxy container {nginxproxy_container.name}")
|
||||||
return ip
|
return ip
|
||||||
|
|
||||||
def docker_container_dns_resolver(domain_name: str) -> Optional[str]:
|
def nginx_proxy_separate_containers_dns_resolver(domain_name):
|
||||||
"""
|
"""
|
||||||
if domain name is of the form "XXX.container.docker" or "anything.XXX.container.docker",
|
if "nginx-proxy" if found in host, return the ip address of the docker container
|
||||||
return the ip address of the docker container named XXX.
|
labeled with "com.github.nginx-proxy.nginx-proxy.nginx".
|
||||||
|
|
||||||
|
:return: IP or None
|
||||||
|
"""
|
||||||
|
log = logging.getLogger('DNS')
|
||||||
|
log.debug(f"nginx_proxy_separate_containers_dns_resolver({domain_name!r})")
|
||||||
|
if 'nginx-proxy' in domain_name:
|
||||||
|
nginx_containers = docker_client.containers.list(filters={"status": "running", "label": "com.github.nginx-proxy.nginx-proxy.nginx"})
|
||||||
|
if len(nginx_containers) == 0:
|
||||||
|
log.info(f"no container labeled with com.github.nginx-proxy.nginx-proxy.nginx found while resolving {domain_name!r}")
|
||||||
|
exited_nginx_containers = docker_client.containers.list(filters={"status": "exited", "label": "com.github.nginx-proxy.nginx-proxy.nginx"})
|
||||||
|
if len(exited_nginx_containers) > 0:
|
||||||
|
exited_nginx_container_logs = exited_nginx_containers[0].logs()
|
||||||
|
log.warning(f"nginx container might have exited unexpectedly. Container logs: " + "\n" + exited_nginx_container_logs.decode())
|
||||||
|
return
|
||||||
|
nginx_container = nginx_containers[0]
|
||||||
|
ip = container_ip(nginx_container)
|
||||||
|
log.info(f"resolving domain name {domain_name!r} as IP address {ip} of nginx container {nginx_container.name}")
|
||||||
|
return ip
|
||||||
|
|
||||||
|
def docker_container_dns_resolver(domain_name):
|
||||||
|
"""
|
||||||
|
if domain name is of the form "XXX.container.docker" or "anything.XXX.container.docker", return the ip address of the docker container
|
||||||
|
named XXX.
|
||||||
|
|
||||||
:return: IP or None
|
:return: IP or None
|
||||||
"""
|
"""
|
||||||
|
@ -232,7 +279,7 @@ def docker_container_dns_resolver(domain_name: str) -> Optional[str]:
|
||||||
match = re.search(r'(^|.+\.)(?P<container>[^.]+)\.container\.docker$', domain_name)
|
match = re.search(r'(^|.+\.)(?P<container>[^.]+)\.container\.docker$', domain_name)
|
||||||
if not match:
|
if not match:
|
||||||
log.debug(f"{domain_name!r} does not match")
|
log.debug(f"{domain_name!r} does not match")
|
||||||
return None
|
return
|
||||||
|
|
||||||
container_name = match.group('container')
|
container_name = match.group('container')
|
||||||
log.debug(f"looking for container {container_name!r}")
|
log.debug(f"looking for container {container_name!r}")
|
||||||
|
@ -240,7 +287,7 @@ def docker_container_dns_resolver(domain_name: str) -> Optional[str]:
|
||||||
container = docker_client.containers.get(container_name)
|
container = docker_client.containers.get(container_name)
|
||||||
except docker.errors.NotFound:
|
except docker.errors.NotFound:
|
||||||
log.warning(f"container named {container_name!r} not found while resolving {domain_name!r}")
|
log.warning(f"container named {container_name!r} not found while resolving {domain_name!r}")
|
||||||
return None
|
return
|
||||||
log.debug(f"container {container.name!r} found ({container.short_id})")
|
log.debug(f"container {container.name!r} found ({container.short_id})")
|
||||||
|
|
||||||
ip = container_ip(container)
|
ip = container_ip(container)
|
||||||
|
@ -252,10 +299,8 @@ def monkey_patch_urllib_dns_resolver():
|
||||||
"""
|
"""
|
||||||
Alter the behavior of the urllib DNS resolver so that any domain name
|
Alter the behavior of the urllib DNS resolver so that any domain name
|
||||||
containing substring 'nginx-proxy' will resolve to the IP address
|
containing substring 'nginx-proxy' will resolve to the IP address
|
||||||
of the container created from image 'nginxproxy/nginx-proxy:test',
|
of the container created from image 'nginxproxy/nginx-proxy:test' or
|
||||||
or to 127.0.0.1 on Darwin.
|
labeled with 'com.github.nginx-proxy.nginx-proxy.nginx'.
|
||||||
|
|
||||||
see https://docs.docker.com/desktop/features/networking/#i-want-to-connect-to-a-container-from-the-host
|
|
||||||
"""
|
"""
|
||||||
prv_getaddrinfo = socket.getaddrinfo
|
prv_getaddrinfo = socket.getaddrinfo
|
||||||
dns_cache = {}
|
dns_cache = {}
|
||||||
|
@ -263,18 +308,19 @@ def monkey_patch_urllib_dns_resolver():
|
||||||
logging.getLogger('DNS').debug(f"resolving domain name {repr(args)}")
|
logging.getLogger('DNS').debug(f"resolving domain name {repr(args)}")
|
||||||
_args = list(args)
|
_args = list(args)
|
||||||
|
|
||||||
# Fail early when querying IP directly, and it is forced ipv6 when not supported,
|
# Fail early when querying IP directly and it is forced ipv6 when not supported,
|
||||||
# Otherwise a pytest container not using the host network fails to pass `test_raw-ip-vhost`.
|
# Otherwise a pytest container not using the host network fails to pass `test_raw-ip-vhost`.
|
||||||
if FORCE_CONTAINER_IPV6 and not HAS_IPV6:
|
if FORCE_CONTAINER_IPV6 and not HAS_IPV6:
|
||||||
pytest.skip("This system does not support IPv6")
|
pytest.skip("This system does not support IPv6")
|
||||||
|
|
||||||
# custom DNS resolvers
|
# custom DNS resolvers
|
||||||
ip = None
|
ip = None
|
||||||
# Docker Desktop can't route traffic directly to Linux containers.
|
|
||||||
if platform.system() == "Darwin":
|
if platform.system() == "Darwin":
|
||||||
ip = "127.0.0.1"
|
ip = "127.0.0.1"
|
||||||
if ip is None:
|
if ip is None:
|
||||||
ip = nginx_proxy_dns_resolver(args[0])
|
ip = nginx_proxy_single_container_dns_resolver(args[0])
|
||||||
|
if ip is None:
|
||||||
|
ip = nginx_proxy_separate_containers_dns_resolver(args[0])
|
||||||
if ip is None:
|
if ip is None:
|
||||||
ip = docker_container_dns_resolver(args[0])
|
ip = docker_container_dns_resolver(args[0])
|
||||||
if ip is not None:
|
if ip is not None:
|
||||||
|
@ -290,12 +336,11 @@ def monkey_patch_urllib_dns_resolver():
|
||||||
socket.getaddrinfo = new_getaddrinfo
|
socket.getaddrinfo = new_getaddrinfo
|
||||||
return prv_getaddrinfo
|
return prv_getaddrinfo
|
||||||
|
|
||||||
|
|
||||||
def restore_urllib_dns_resolver(getaddrinfo_func):
|
def restore_urllib_dns_resolver(getaddrinfo_func):
|
||||||
socket.getaddrinfo = getaddrinfo_func
|
socket.getaddrinfo = getaddrinfo_func
|
||||||
|
|
||||||
|
|
||||||
def get_nginx_conf_from_container(container: Container) -> bytes:
|
def get_nginx_conf_from_container(container):
|
||||||
"""
|
"""
|
||||||
return the nginx /etc/nginx/conf.d/default.conf file content from a container
|
return the nginx /etc/nginx/conf.d/default.conf file content from a container
|
||||||
"""
|
"""
|
||||||
|
@ -310,102 +355,84 @@ def get_nginx_conf_from_container(container: Container) -> bytes:
|
||||||
return conffile.read()
|
return conffile.read()
|
||||||
|
|
||||||
|
|
||||||
def __prepare_and_execute_compose_cmd(compose_files: List[str], project_name: str, cmd: str):
|
def docker_compose_up(project_name, compose_file='docker-compose.yml'):
|
||||||
"""
|
composeCmd = f'{DOCKER_COMPOSE} --project-name {project_name} --file {compose_file} up --remove-orphans --force-recreate --detach'
|
||||||
Prepare and execute the Docker Compose command with the provided compose files and project name.
|
logging.info(composeCmd)
|
||||||
"""
|
|
||||||
compose_cmd = StringIO()
|
|
||||||
compose_cmd.write(DOCKER_COMPOSE)
|
|
||||||
compose_cmd.write(f" --project-name {project_name}")
|
|
||||||
for compose_file in compose_files:
|
|
||||||
compose_cmd.write(f" --file {compose_file}")
|
|
||||||
compose_cmd.write(f" {cmd}")
|
|
||||||
|
|
||||||
logging.info(compose_cmd.getvalue())
|
|
||||||
try:
|
try:
|
||||||
subprocess.check_output(shlex.split(compose_cmd.getvalue()), stderr=subprocess.STDOUT)
|
subprocess.check_output(shlex.split(composeCmd), stderr=subprocess.STDOUT)
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
pytest.fail(f"Error while running '{compose_cmd.getvalue()}':\n{e.output}", pytrace=False)
|
pytest.fail(f"Error while runninng '{composeCmd}:\n{e.output}", pytrace=False)
|
||||||
|
|
||||||
|
|
||||||
def docker_compose_up(compose_files: List[str], project_name: str):
|
def docker_compose_down(project_name, compose_file='docker-compose.yml'):
|
||||||
"""
|
composeCmd = f'{DOCKER_COMPOSE} --project-name {project_name} --file {compose_file} down --remove-orphans --volumes'
|
||||||
Execute compose up --detach with the provided compose files and project name.
|
logging.info(composeCmd)
|
||||||
"""
|
|
||||||
if compose_files is None or len(compose_files) == 0:
|
|
||||||
pytest.fail(f"No compose file passed to docker_compose_up", pytrace=False)
|
|
||||||
__prepare_and_execute_compose_cmd(compose_files, project_name, cmd="up --detach")
|
|
||||||
|
|
||||||
|
try:
|
||||||
def docker_compose_down(compose_files: List[str], project_name: str):
|
subprocess.check_output(shlex.split(composeCmd), stderr=subprocess.STDOUT)
|
||||||
"""
|
except subprocess.CalledProcessError as e:
|
||||||
Execute compose down --volumes with the provided compose files and project name.
|
pytest.fail(f"Error while runninng '{composeCmd}':\n{e.output}", pytrace=False)
|
||||||
"""
|
|
||||||
if compose_files is None or len(compose_files) == 0:
|
|
||||||
pytest.fail(f"No compose file passed to docker_compose_up", pytrace=False)
|
|
||||||
__prepare_and_execute_compose_cmd(compose_files, project_name, cmd="down --volumes")
|
|
||||||
|
|
||||||
|
|
||||||
def wait_for_nginxproxy_to_be_ready():
|
def wait_for_nginxproxy_to_be_ready():
|
||||||
"""
|
"""
|
||||||
If one (and only one) container started from image nginxproxy/nginx-proxy:test is found,
|
If one (and only one) container started from image nginxproxy/nginx-proxy:test
|
||||||
wait for its log to contain substring "Watching docker events"
|
or nginxproxy/nginx-proxy:test-dockergen is found, wait for its log to contain
|
||||||
|
substring "Watching docker events"
|
||||||
"""
|
"""
|
||||||
containers = docker_client.containers.list(filters={"ancestor": "nginxproxy/nginx-proxy:test"})
|
timeout = time.time() + 10
|
||||||
if len(containers) != 1:
|
while True:
|
||||||
return
|
containers = docker_client.containers.list(
|
||||||
container = containers[0]
|
filters={"status": "running", "ancestor": f"nginxproxy/nginx-proxy:{IMAGE_TAG}"}
|
||||||
for line in container.logs(stream=True):
|
)
|
||||||
if b"Watching docker events" in line:
|
if len(containers) == 1:
|
||||||
logging.debug("nginx-proxy ready")
|
|
||||||
break
|
break
|
||||||
|
if time.time() > timeout:
|
||||||
|
pytest.fail(f"Got {len(containers)} nginxproxy/nginx-proxy:{IMAGE_TAG} containers after 10s", pytrace=False)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
container = containers
|
||||||
|
conf_generated = False
|
||||||
|
while True:
|
||||||
|
for line in container[0].logs(stream=True, follow=True):
|
||||||
|
if b"Generated '/etc/nginx/conf.d/default.conf'" in line:
|
||||||
|
return
|
||||||
|
if time.time() > timeout:
|
||||||
|
pytest.fail(f"nginxproxy/nginx-proxy:{IMAGE_TAG} container not ready after 10s", pytrace=False)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def docker_compose_files(request: FixtureRequest) -> List[str]:
|
def docker_compose_file(request):
|
||||||
"""Fixture returning the docker compose files to consider:
|
"""Fixture naming the docker compose file to consider.
|
||||||
|
|
||||||
If a YAML file exists with the same name as the test module (with the `.py` extension
|
If a YAML file exists with the same name as the test module (with the `.py` extension replaced
|
||||||
replaced with `.base.yml`, ie `test_foo.py`-> `test_foo.base.yml`) and in the same
|
with `.yml` or `.yaml`), use that. Otherwise, use `docker-compose.yml` in the same directory
|
||||||
directory as the test module, use only that file.
|
as the test module.
|
||||||
|
|
||||||
Otherwise, merge the following files in this order:
|
|
||||||
|
|
||||||
- the `compose.base.yml` file in the parent `test` directory.
|
|
||||||
- if present in the same directory as the test module, the `compose.base.override.yml` file.
|
|
||||||
- the YAML file named after the current test module (ie `test_foo.py`-> `test_foo.yml`)
|
|
||||||
|
|
||||||
Tests can override this fixture to specify a custom location.
|
Tests can override this fixture to specify a custom location.
|
||||||
"""
|
"""
|
||||||
compose_files: List[str] = []
|
test_module_dir = os.path.dirname(request.module.__file__)
|
||||||
test_module_path = pathlib.Path(request.module.__file__).parent
|
yml_file = os.path.join(test_module_dir, request.module.__name__ + '.yml')
|
||||||
|
yaml_file = os.path.join(test_module_dir, request.module.__name__ + '.yaml')
|
||||||
|
default_file = os.path.join(test_module_dir, 'docker-compose.yml')
|
||||||
|
|
||||||
module_base_file = test_module_path.joinpath(f"{request.module.__name__}.base.yml")
|
if os.path.isfile(yml_file):
|
||||||
if module_base_file.is_file():
|
docker_compose_file = yml_file
|
||||||
return [module_base_file.as_posix()]
|
elif os.path.isfile(yaml_file):
|
||||||
|
docker_compose_file = yaml_file
|
||||||
|
else:
|
||||||
|
docker_compose_file = default_file
|
||||||
|
|
||||||
global_base_file = test_module_path.parent.joinpath("compose.base.yml")
|
if not os.path.isfile(docker_compose_file):
|
||||||
if global_base_file.is_file():
|
logging.error("Could not find any docker compose file named either '{0}.yml', '{0}.yaml' or 'docker-compose.yml'".format(request.module.__name__))
|
||||||
compose_files.append(global_base_file.as_posix())
|
|
||||||
|
|
||||||
module_base_override_file = test_module_path.joinpath("compose.base.override.yml")
|
logging.info(f"using docker compose file {docker_compose_file}")
|
||||||
if module_base_override_file.is_file():
|
yield docker_compose_file
|
||||||
compose_files.append(module_base_override_file.as_posix())
|
|
||||||
|
|
||||||
module_compose_file = test_module_path.joinpath(f"{request.module.__name__}.yml")
|
|
||||||
if module_compose_file.is_file():
|
|
||||||
compose_files.append(module_compose_file.as_posix())
|
|
||||||
|
|
||||||
if not module_base_file.is_file() and not module_compose_file.is_file():
|
|
||||||
logging.error(
|
|
||||||
f"Could not find any docker compose file named '{module_base_file.name}' or '{module_compose_file.name}'"
|
|
||||||
)
|
|
||||||
|
|
||||||
logging.debug(f"using docker compose files {compose_files}")
|
|
||||||
return compose_files
|
|
||||||
|
|
||||||
|
|
||||||
def connect_to_network(network: Network) -> Optional[Network]:
|
def connect_to_network(network):
|
||||||
"""
|
"""
|
||||||
If we are running from a container, connect our container to the given network
|
If we are running from a container, connect our container to the given network
|
||||||
|
|
||||||
|
@ -416,7 +443,7 @@ def connect_to_network(network: Network) -> Optional[Network]:
|
||||||
my_container = docker_client.containers.get(test_container)
|
my_container = docker_client.containers.get(test_container)
|
||||||
except docker.errors.NotFound:
|
except docker.errors.NotFound:
|
||||||
logging.warning(f"container {test_container} not found")
|
logging.warning(f"container {test_container} not found")
|
||||||
return None
|
return
|
||||||
|
|
||||||
# figure out our container networks
|
# figure out our container networks
|
||||||
my_networks = list(my_container.attrs["NetworkSettings"]["Networks"].keys())
|
my_networks = list(my_container.attrs["NetworkSettings"]["Networks"].keys())
|
||||||
|
@ -433,7 +460,7 @@ def connect_to_network(network: Network) -> Optional[Network]:
|
||||||
return network
|
return network
|
||||||
|
|
||||||
|
|
||||||
def disconnect_from_network(network: Network = None):
|
def disconnect_from_network(network=None):
|
||||||
"""
|
"""
|
||||||
If we are running from a container, disconnect our container from the given network.
|
If we are running from a container, disconnect our container from the given network.
|
||||||
|
|
||||||
|
@ -455,7 +482,7 @@ def disconnect_from_network(network: Network = None):
|
||||||
network.disconnect(my_container)
|
network.disconnect(my_container)
|
||||||
|
|
||||||
|
|
||||||
def connect_to_all_networks() -> List[Network]:
|
def connect_to_all_networks():
|
||||||
"""
|
"""
|
||||||
If we are running from a container, connect our container to all current docker networks.
|
If we are running from a container, connect our container to all current docker networks.
|
||||||
|
|
||||||
|
@ -472,32 +499,30 @@ def connect_to_all_networks() -> List[Network]:
|
||||||
class DockerComposer(contextlib.AbstractContextManager):
|
class DockerComposer(contextlib.AbstractContextManager):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._networks = None
|
self._networks = None
|
||||||
self._docker_compose_files = None
|
self._docker_compose_file = None
|
||||||
self._project_name = None
|
self._project_name = None
|
||||||
|
|
||||||
def __exit__(self, *exc_info):
|
def __exit__(self, *exc_info):
|
||||||
self._down()
|
self._down()
|
||||||
|
|
||||||
def _down(self):
|
def _down(self):
|
||||||
if self._docker_compose_files is None:
|
if self._docker_compose_file is None:
|
||||||
return
|
return
|
||||||
for network in self._networks:
|
for network in self._networks:
|
||||||
disconnect_from_network(network)
|
disconnect_from_network(network)
|
||||||
docker_compose_down(self._docker_compose_files, self._project_name)
|
docker_compose_down(self._project_name, self._docker_compose_file)
|
||||||
self._docker_compose_file = None
|
self._docker_compose_file = None
|
||||||
self._project_name = None
|
|
||||||
|
|
||||||
def compose(self, docker_compose_files: List[str], project_name: str):
|
def compose(self, project_name, docker_compose_file):
|
||||||
if docker_compose_files == self._docker_compose_files and project_name == self._project_name:
|
if docker_compose_file == self._docker_compose_file and project_name == self._project_name:
|
||||||
return
|
return
|
||||||
self._down()
|
self._down()
|
||||||
if docker_compose_files is None or project_name is None:
|
if docker_compose_file is None:
|
||||||
return
|
return
|
||||||
docker_compose_up(docker_compose_files, project_name)
|
docker_compose_up(project_name, docker_compose_file)
|
||||||
self._networks = connect_to_all_networks()
|
self._networks = connect_to_all_networks()
|
||||||
wait_for_nginxproxy_to_be_ready()
|
wait_for_nginxproxy_to_be_ready()
|
||||||
time.sleep(3) # give time to containers to be ready
|
self._docker_compose_file = docker_compose_file
|
||||||
self._docker_compose_files = docker_compose_files
|
|
||||||
self._project_name = project_name
|
self._project_name = project_name
|
||||||
|
|
||||||
|
|
||||||
|
@ -509,14 +534,14 @@ class DockerComposer(contextlib.AbstractContextManager):
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def docker_composer() -> Iterator[DockerComposer]:
|
def docker_composer():
|
||||||
with DockerComposer() as d:
|
with DockerComposer() as d:
|
||||||
yield d
|
yield d
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def ca_root_certificate() -> str:
|
def ca_root_certificate():
|
||||||
return CA_ROOT_CERTIFICATE.as_posix()
|
yield CA_ROOT_CERTIFICATE
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -527,38 +552,26 @@ def monkey_patched_dns():
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def docker_compose(
|
def docker_compose(request, monkey_patched_dns, docker_composer, docker_compose_file):
|
||||||
request: FixtureRequest,
|
"""Ensures containers described in a docker compose file are started.
|
||||||
monkeypatch,
|
|
||||||
monkey_patched_dns,
|
A custom docker compose file name can be specified by overriding the `docker_compose_file`
|
||||||
docker_composer,
|
fixture.
|
||||||
docker_compose_files
|
|
||||||
) -> Iterator[DockerClient]:
|
Also, in the case where pytest is running from a docker container, this fixture makes sure
|
||||||
|
our container will be attached to all the docker networks.
|
||||||
"""
|
"""
|
||||||
Ensures containers necessary for the test module are started in a compose project,
|
|
||||||
and set the environment variable `PYTEST_MODULE_PATH` to the test module's parent folder.
|
|
||||||
|
|
||||||
A list of custom docker compose files path can be specified by overriding
|
|
||||||
the `docker_compose_file` fixture.
|
|
||||||
|
|
||||||
Also, in the case where pytest is running from a docker container, this fixture
|
|
||||||
makes sure our container will be attached to all the docker networks.
|
|
||||||
"""
|
|
||||||
pytest_module_path = pathlib.Path(request.module.__file__).parent
|
|
||||||
monkeypatch.setenv("PYTEST_MODULE_PATH", pytest_module_path.as_posix())
|
|
||||||
|
|
||||||
project_name = request.module.__name__
|
project_name = request.module.__name__
|
||||||
docker_composer.compose(docker_compose_files, project_name)
|
docker_composer.compose(project_name, docker_compose_file)
|
||||||
|
|
||||||
yield docker_client
|
yield docker_client
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture()
|
||||||
def nginxproxy() -> Iterator[RequestsForDocker]:
|
def nginxproxy():
|
||||||
"""
|
"""
|
||||||
Provides the `nginxproxy` object that can be used in the same way the requests module is:
|
Provides the `nginxproxy` object that can be used in the same way the requests module is:
|
||||||
|
|
||||||
r = nginxproxy.get("https://foo.com")
|
r = nginxproxy.get("http://foo.com")
|
||||||
|
|
||||||
The difference is that in case an HTTP requests has status code 404 or 502 (which mostly
|
The difference is that in case an HTTP requests has status code 404 or 502 (which mostly
|
||||||
indicates that nginx has just reloaded), we retry up to 30 times the query.
|
indicates that nginx has just reloaded), we retry up to 30 times the query.
|
||||||
|
@ -567,15 +580,15 @@ def nginxproxy() -> Iterator[RequestsForDocker]:
|
||||||
made against containers to use the containers IPv6 address when set to `True`. If IPv6 is not
|
made against containers to use the containers IPv6 address when set to `True`. If IPv6 is not
|
||||||
supported by the system or docker, that particular test will be skipped.
|
supported by the system or docker, that particular test will be skipped.
|
||||||
"""
|
"""
|
||||||
yield RequestsForDocker()
|
yield requests_for_docker()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture()
|
||||||
def acme_challenge_path() -> str:
|
def acme_challenge_path():
|
||||||
"""
|
"""
|
||||||
Provides fake Let's Encrypt ACME challenge path used in certain tests
|
Provides fake Let's Encrypt ACME challenge path used in certain tests
|
||||||
"""
|
"""
|
||||||
return ".well-known/acme-challenge/test-filename"
|
yield ".well-known/acme-challenge/test-filename"
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
#
|
#
|
||||||
|
@ -583,10 +596,14 @@ def acme_challenge_path() -> str:
|
||||||
#
|
#
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
# pytest hook to display additional stuff in test report
|
# pytest hook to display additionnal stuff in test report
|
||||||
def pytest_runtest_logreport(report):
|
def pytest_runtest_logreport(report):
|
||||||
if report.failed:
|
if report.failed:
|
||||||
test_containers = docker_client.containers.list(all=True, filters={"ancestor": "nginxproxy/nginx-proxy:test"})
|
nginx_containers = docker_client.containers.list(all=True, filters={"label": "com.github.nginx-proxy.nginx-proxy.nginx"})
|
||||||
|
for container in nginx_containers:
|
||||||
|
report.longrepr.addsection('nginx container logs', container.logs().decode())
|
||||||
|
|
||||||
|
test_containers = docker_client.containers.list(all=True, filters={"ancestor": f"nginxproxy/nginx-proxy:{IMAGE_TAG}"})
|
||||||
for container in test_containers:
|
for container in test_containers:
|
||||||
report.longrepr.addsection('nginx-proxy logs', container.logs().decode())
|
report.longrepr.addsection('nginx-proxy logs', container.logs().decode())
|
||||||
report.longrepr.addsection('nginx-proxy conf', get_nginx_conf_from_container(container).decode())
|
report.longrepr.addsection('nginx-proxy conf', get_nginx_conf_from_container(container).decode())
|
||||||
|
@ -612,9 +629,9 @@ def pytest_runtest_setup(item):
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
try:
|
try:
|
||||||
docker_client.images.get('nginxproxy/nginx-proxy:test')
|
docker_client.images.get(f"nginxproxy/nginx-proxy:{IMAGE_TAG}")
|
||||||
except docker.errors.ImageNotFound:
|
except docker.errors.ImageNotFound:
|
||||||
pytest.exit("The docker image 'nginxproxy/nginx-proxy:test' is missing")
|
pytest.exit(f"The docker image 'nginxproxy/nginx-proxy:{IMAGE_TAG}' is missing")
|
||||||
|
|
||||||
if Version(docker.__version__) < Version("7.0.0"):
|
if Version(docker.__version__) < Version("7.0.0"):
|
||||||
pytest.exit("This test suite is meant to work with the python docker module v7.0.0 or later")
|
pytest.exit("This test suite is meant to work with the python docker module v7.0.0 or later")
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
# #
|
# #
|
||||||
# This script is meant to run the test suite from a Docker container. #
|
# This script is meant to run the test suite from a Docker container. #
|
||||||
# #
|
# #
|
||||||
# This is useful when you want to run the test suite from Mac or #
|
# This is usefull when you want to run the test suite from Mac or #
|
||||||
# Docker Toolbox. #
|
# Docker Toolbox. #
|
||||||
# #
|
# #
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
backoff==2.2.1
|
backoff==2.2.1
|
||||||
docker==7.1.0
|
docker==7.1.0
|
||||||
packaging==24.2
|
|
||||||
pytest==8.3.4
|
pytest==8.3.4
|
||||||
requests==2.32.3
|
requests==2.32.3
|
||||||
urllib3==2.3.0
|
packaging==24.2
|
|
@ -28,7 +28,7 @@ class Handler(http.server.SimpleHTTPRequestHandler):
|
||||||
self.send_header("Content-Type", "text/plain")
|
self.send_header("Content-Type", "text/plain")
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
|
|
||||||
if len(response_body):
|
if (len(response_body)):
|
||||||
self.wfile.write(response_body.encode())
|
self.wfile.write(response_body.encode())
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
1
test/stress_tests/README.md
Normal file
1
test/stress_tests/README.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
This directory contains tests that showcase scenarios known to break the expected behavior of nginx-proxy.
|
|
@ -4,16 +4,6 @@ import pytest
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
|
||||||
def test_default_nginx_welcome_page_should_not_be_served(docker_compose, nginxproxy):
|
|
||||||
r = nginxproxy.get("http://whatever.nginx-proxy/", allow_redirects=False)
|
|
||||||
assert "<title>Welcome to nginx!</title>" not in r.text
|
|
||||||
|
|
||||||
|
|
||||||
def test_unknown_virtual_host_is_503(docker_compose, nginxproxy):
|
|
||||||
r = nginxproxy.get("http://unknown.nginx-proxy/", allow_redirects=False)
|
|
||||||
assert r.status_code == 503
|
|
||||||
|
|
||||||
|
|
||||||
def test_http_web_a_is_forwarded(docker_compose, nginxproxy):
|
def test_http_web_a_is_forwarded(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("http://webA.nginx-proxy/port", allow_redirects=False)
|
r = nginxproxy.get("http://webA.nginx-proxy/port", allow_redirects=False)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
@ -21,12 +11,22 @@ def test_http_web_a_is_forwarded(docker_compose, nginxproxy):
|
||||||
|
|
||||||
|
|
||||||
def test_http_web_b_gets_an_error(docker_compose, nginxproxy):
|
def test_http_web_b_gets_an_error(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("http://webB.nginx-proxy/", allow_redirects=False)
|
r = nginxproxy.get("http://webB.nginx-proxy/", allow_redirects=False, expected_status_code=502)
|
||||||
assert "<title>Welcome to nginx!</title>" not in r.text
|
assert "<title>Welcome to nginx!</title>" not in r.text
|
||||||
with pytest.raises(requests.exceptions.HTTPError):
|
with pytest.raises(requests.exceptions.HTTPError):
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
|
|
||||||
|
|
||||||
|
def test_default_nginx_welcome_page_should_not_be_served(docker_compose, nginxproxy):
|
||||||
|
r = nginxproxy.get("http://whatever.nginx-proxy/", allow_redirects=False, expected_status_code=503)
|
||||||
|
assert "<title>Welcome to nginx!</title>" not in r.text
|
||||||
|
|
||||||
|
|
||||||
|
def test_unknown_virtual_host_is_503(docker_compose, nginxproxy):
|
||||||
|
r = nginxproxy.get("http://unknown.nginx-proxy/", allow_redirects=False, expected_status_code=503)
|
||||||
|
assert r.status_code == 503
|
||||||
|
|
||||||
|
|
||||||
def test_reverseproxy_survive_restart(docker_compose):
|
def test_reverseproxy_survive_restart(docker_compose):
|
||||||
docker_compose.containers.get("reverseproxy").restart()
|
docker_compose.containers.get("reverseproxy").restart()
|
||||||
sleep(2) # give time for the container to initialize
|
sleep(2) # give time for the container to initialize
|
|
@ -0,0 +1,63 @@
|
||||||
|
networks:
|
||||||
|
netA:
|
||||||
|
netB:
|
||||||
|
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
nginx_conf:
|
||||||
|
|
||||||
|
|
||||||
|
services:
|
||||||
|
reverseproxy:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
container_name: reverseproxy
|
||||||
|
networks:
|
||||||
|
- netA
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
|
volumes:
|
||||||
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
|
|
||||||
|
docker-gen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
networks:
|
||||||
|
- netA
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
environment:
|
||||||
|
NGINX_CONTAINER_NAME: reverseproxy
|
||||||
|
|
||||||
|
reverseproxynginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: reverseproxy
|
||||||
|
networks:
|
||||||
|
- netA
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
||||||
|
|
||||||
|
webA:
|
||||||
|
networks:
|
||||||
|
- netA
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- 81
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: 81
|
||||||
|
VIRTUAL_HOST: webA.nginx-proxy
|
||||||
|
|
||||||
|
webB:
|
||||||
|
networks:
|
||||||
|
- netB
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- 82
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: 82
|
||||||
|
VIRTUAL_HOST: webB.nginx-proxy
|
|
@ -1,6 +0,0 @@
|
||||||
services:
|
|
||||||
nginx-proxy:
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
|
||||||
- ${PYTEST_MODULE_PATH}/certs:/etc/nginx/certs:ro
|
|
||||||
- ${PYTEST_MODULE_PATH}/acme_root:/usr/share/nginx/html:ro
|
|
|
@ -1,40 +0,0 @@
|
||||||
services:
|
|
||||||
nginx-proxy:
|
|
||||||
environment:
|
|
||||||
ACME_HTTP_CHALLENGE_LOCATION: "false"
|
|
||||||
|
|
||||||
web1:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "81"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "81"
|
|
||||||
VIRTUAL_HOST: "web1.nginx-proxy.tld"
|
|
||||||
|
|
||||||
web2:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "82"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "82"
|
|
||||||
VIRTUAL_HOST: "web2.nginx-proxy.tld"
|
|
||||||
ACME_HTTP_CHALLENGE_LOCATION: "true"
|
|
||||||
|
|
||||||
web3:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "83"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "83"
|
|
||||||
VIRTUAL_HOST: "web3.nginx-proxy.tld"
|
|
||||||
HTTPS_METHOD: noredirect
|
|
||||||
|
|
||||||
web4:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "84"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "84"
|
|
||||||
VIRTUAL_HOST: "web4.nginx-proxy.tld"
|
|
||||||
HTTPS_METHOD: noredirect
|
|
||||||
ACME_HTTP_CHALLENGE_LOCATION: "true"
|
|
|
@ -1,36 +0,0 @@
|
||||||
services:
|
|
||||||
web1:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "81"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "81"
|
|
||||||
VIRTUAL_HOST: "web1.nginx-proxy.tld"
|
|
||||||
|
|
||||||
web2:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "82"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "82"
|
|
||||||
VIRTUAL_HOST: "web2.nginx-proxy.tld"
|
|
||||||
ACME_HTTP_CHALLENGE_LOCATION: "false"
|
|
||||||
|
|
||||||
web3:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "83"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "83"
|
|
||||||
VIRTUAL_HOST: "web3.nginx-proxy.tld"
|
|
||||||
HTTPS_METHOD: noredirect
|
|
||||||
|
|
||||||
web4:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "84"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "84"
|
|
||||||
VIRTUAL_HOST: "web4.nginx-proxy.tld"
|
|
||||||
HTTPS_METHOD: noredirect
|
|
||||||
ACME_HTTP_CHALLENGE_LOCATION: "false"
|
|
|
@ -1,21 +0,0 @@
|
||||||
services:
|
|
||||||
nginx-proxy:
|
|
||||||
environment:
|
|
||||||
ACME_HTTP_CHALLENGE_LOCATION: "legacy"
|
|
||||||
|
|
||||||
web1:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "81"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "81"
|
|
||||||
VIRTUAL_HOST: "web1.nginx-proxy.tld"
|
|
||||||
|
|
||||||
web2:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "82"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "82"
|
|
||||||
VIRTUAL_HOST: "web2.nginx-proxy.tld"
|
|
||||||
HTTPS_METHOD: noredirect
|
|
|
@ -1,27 +1,29 @@
|
||||||
def test_redirect_acme_challenge_location_enabled(docker_compose, nginxproxy, acme_challenge_path):
|
|
||||||
r = nginxproxy.get(
|
|
||||||
f"http://web1.nginx-proxy.tld/{acme_challenge_path}",
|
|
||||||
allow_redirects=False
|
|
||||||
)
|
|
||||||
assert r.status_code == 200
|
|
||||||
|
|
||||||
def test_redirect_acme_challenge_location_disabled(docker_compose, nginxproxy, acme_challenge_path):
|
def test_redirect_acme_challenge_location_disabled(docker_compose, nginxproxy, acme_challenge_path):
|
||||||
r = nginxproxy.get(
|
r = nginxproxy.get(
|
||||||
f"http://web2.nginx-proxy.tld/{acme_challenge_path}",
|
f"http://web1.nginx-proxy.tld/{acme_challenge_path}",
|
||||||
allow_redirects=False
|
allow_redirects=False,
|
||||||
|
expected_status_code=301
|
||||||
)
|
)
|
||||||
assert r.status_code == 301
|
assert r.status_code == 301
|
||||||
|
|
||||||
def test_noredirect_acme_challenge_location_enabled(docker_compose, nginxproxy, acme_challenge_path):
|
def test_redirect_acme_challenge_location_enabled(docker_compose, nginxproxy, acme_challenge_path):
|
||||||
r = nginxproxy.get(
|
r = nginxproxy.get(
|
||||||
f"http://web3.nginx-proxy.tld/{acme_challenge_path}",
|
f"http://web2.nginx-proxy.tld/{acme_challenge_path}",
|
||||||
allow_redirects=False
|
allow_redirects=False
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
|
||||||
def test_noredirect_acme_challenge_location_disabled(docker_compose, nginxproxy, acme_challenge_path):
|
def test_noredirect_acme_challenge_location_disabled(docker_compose, nginxproxy, acme_challenge_path):
|
||||||
|
r = nginxproxy.get(
|
||||||
|
f"http://web3.nginx-proxy.tld/{acme_challenge_path}",
|
||||||
|
allow_redirects=False,
|
||||||
|
expected_status_code=404
|
||||||
|
)
|
||||||
|
assert r.status_code == 404
|
||||||
|
|
||||||
|
def test_noredirect_acme_challenge_location_enabled(docker_compose, nginxproxy, acme_challenge_path):
|
||||||
r = nginxproxy.get(
|
r = nginxproxy.get(
|
||||||
f"http://web4.nginx-proxy.tld/{acme_challenge_path}",
|
f"http://web4.nginx-proxy.tld/{acme_challenge_path}",
|
||||||
allow_redirects=False
|
allow_redirects=False
|
||||||
)
|
)
|
||||||
assert r.status_code == 404
|
assert r.status_code == 200
|
|
@ -0,0 +1,73 @@
|
||||||
|
volumes:
|
||||||
|
nginx_conf:
|
||||||
|
|
||||||
|
|
||||||
|
services:
|
||||||
|
web1:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "81"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: "81"
|
||||||
|
VIRTUAL_HOST: "web1.nginx-proxy.tld"
|
||||||
|
|
||||||
|
web2:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "82"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: "82"
|
||||||
|
VIRTUAL_HOST: "web2.nginx-proxy.tld"
|
||||||
|
ACME_HTTP_CHALLENGE_LOCATION: "true"
|
||||||
|
|
||||||
|
web3:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "83"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: "83"
|
||||||
|
VIRTUAL_HOST: "web3.nginx-proxy.tld"
|
||||||
|
HTTPS_METHOD: noredirect
|
||||||
|
|
||||||
|
web4:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "84"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: "84"
|
||||||
|
VIRTUAL_HOST: "web4.nginx-proxy.tld"
|
||||||
|
HTTPS_METHOD: noredirect
|
||||||
|
ACME_HTTP_CHALLENGE_LOCATION: "true"
|
||||||
|
|
||||||
|
sut:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
|
environment: &nginxProxyEnv
|
||||||
|
ACME_HTTP_CHALLENGE_LOCATION: "false"
|
||||||
|
volumes:
|
||||||
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
|
- &certs ./certs:/etc/nginx/certs:ro
|
||||||
|
- &acmeRoot ./acme_root:/usr/share/nginx/html:ro
|
||||||
|
|
||||||
|
sutdockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
environment: *nginxProxyEnv
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
- *certs
|
||||||
|
|
||||||
|
sutnginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
- *certs
|
||||||
|
- *acmeRoot
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
|
@ -1,27 +1,29 @@
|
||||||
def test_redirect_acme_challenge_location_disabled(docker_compose, nginxproxy, acme_challenge_path):
|
def test_redirect_acme_challenge_location_enabled(docker_compose, nginxproxy, acme_challenge_path):
|
||||||
r = nginxproxy.get(
|
r = nginxproxy.get(
|
||||||
f"http://web1.nginx-proxy.tld/{acme_challenge_path}",
|
f"http://web1.nginx-proxy.tld/{acme_challenge_path}",
|
||||||
allow_redirects=False
|
allow_redirects=False
|
||||||
)
|
)
|
||||||
assert r.status_code == 301
|
assert r.status_code == 200
|
||||||
|
|
||||||
def test_redirect_acme_challenge_location_enabled(docker_compose, nginxproxy, acme_challenge_path):
|
def test_redirect_acme_challenge_location_disabled(docker_compose, nginxproxy, acme_challenge_path):
|
||||||
r = nginxproxy.get(
|
r = nginxproxy.get(
|
||||||
f"http://web2.nginx-proxy.tld/{acme_challenge_path}",
|
f"http://web2.nginx-proxy.tld/{acme_challenge_path}",
|
||||||
|
allow_redirects=False,
|
||||||
|
expected_status_code=301
|
||||||
|
)
|
||||||
|
assert r.status_code == 301
|
||||||
|
|
||||||
|
def test_noredirect_acme_challenge_location_enabled(docker_compose, nginxproxy, acme_challenge_path):
|
||||||
|
r = nginxproxy.get(
|
||||||
|
f"http://web3.nginx-proxy.tld/{acme_challenge_path}",
|
||||||
allow_redirects=False
|
allow_redirects=False
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
|
||||||
def test_noredirect_acme_challenge_location_disabled(docker_compose, nginxproxy, acme_challenge_path):
|
def test_noredirect_acme_challenge_location_disabled(docker_compose, nginxproxy, acme_challenge_path):
|
||||||
r = nginxproxy.get(
|
r = nginxproxy.get(
|
||||||
f"http://web3.nginx-proxy.tld/{acme_challenge_path}",
|
f"http://web4.nginx-proxy.tld/{acme_challenge_path}",
|
||||||
allow_redirects=False
|
allow_redirects=False,
|
||||||
|
expected_status_code=404
|
||||||
)
|
)
|
||||||
assert r.status_code == 404
|
assert r.status_code == 404
|
||||||
|
|
||||||
def test_noredirect_acme_challenge_location_enabled(docker_compose, nginxproxy, acme_challenge_path):
|
|
||||||
r = nginxproxy.get(
|
|
||||||
f"http://web4.nginx-proxy.tld/{acme_challenge_path}",
|
|
||||||
allow_redirects=False
|
|
||||||
)
|
|
||||||
assert r.status_code == 200
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
volumes:
|
||||||
|
nginx_conf:
|
||||||
|
|
||||||
|
|
||||||
|
services:
|
||||||
|
web1:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "81"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: "81"
|
||||||
|
VIRTUAL_HOST: "web1.nginx-proxy.tld"
|
||||||
|
|
||||||
|
web2:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "82"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: "82"
|
||||||
|
VIRTUAL_HOST: "web2.nginx-proxy.tld"
|
||||||
|
ACME_HTTP_CHALLENGE_LOCATION: "false"
|
||||||
|
|
||||||
|
web3:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "83"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: "83"
|
||||||
|
VIRTUAL_HOST: "web3.nginx-proxy.tld"
|
||||||
|
HTTPS_METHOD: noredirect
|
||||||
|
|
||||||
|
web4:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "84"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: "84"
|
||||||
|
VIRTUAL_HOST: "web4.nginx-proxy.tld"
|
||||||
|
HTTPS_METHOD: noredirect
|
||||||
|
ACME_HTTP_CHALLENGE_LOCATION: "false"
|
||||||
|
|
||||||
|
sut:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
|
volumes:
|
||||||
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
|
- &certs ./certs:/etc/nginx/certs:ro
|
||||||
|
- &acmeRoot ./acme_root:/usr/share/nginx/html:ro
|
||||||
|
|
||||||
|
sutdockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
- *certs
|
||||||
|
|
||||||
|
sutnginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
- *certs
|
||||||
|
- *acmeRoot
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
|
@ -8,6 +8,7 @@ def test_redirect_acme_challenge_location_legacy(docker_compose, nginxproxy, acm
|
||||||
def test_noredirect_acme_challenge_location_legacy(docker_compose, nginxproxy, acme_challenge_path):
|
def test_noredirect_acme_challenge_location_legacy(docker_compose, nginxproxy, acme_challenge_path):
|
||||||
r = nginxproxy.get(
|
r = nginxproxy.get(
|
||||||
f"http://web2.nginx-proxy.tld/{acme_challenge_path}",
|
f"http://web2.nginx-proxy.tld/{acme_challenge_path}",
|
||||||
allow_redirects=False
|
allow_redirects=False,
|
||||||
|
expected_status_code=404
|
||||||
)
|
)
|
||||||
assert r.status_code == 404
|
assert r.status_code == 404
|
|
@ -0,0 +1,54 @@
|
||||||
|
volumes:
|
||||||
|
nginx_conf:
|
||||||
|
|
||||||
|
|
||||||
|
services:
|
||||||
|
web1:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "81"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: "81"
|
||||||
|
VIRTUAL_HOST: "web1.nginx-proxy.tld"
|
||||||
|
|
||||||
|
web2:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "82"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: "82"
|
||||||
|
VIRTUAL_HOST: "web2.nginx-proxy.tld"
|
||||||
|
HTTPS_METHOD: noredirect
|
||||||
|
|
||||||
|
sut:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
|
environment: &nginxProxyEnv
|
||||||
|
ACME_HTTP_CHALLENGE_LOCATION: "legacy"
|
||||||
|
volumes:
|
||||||
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
|
- &certs ./certs:/etc/nginx/certs:ro
|
||||||
|
- &acmeRoot ./acme_root:/usr/share/nginx/html:ro
|
||||||
|
|
||||||
|
sutdockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
environment: *nginxProxyEnv
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
- *certs
|
||||||
|
|
||||||
|
sutnginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
- *certs
|
||||||
|
- *acmeRoot
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
|
@ -1,25 +1,22 @@
|
||||||
"""
|
"""
|
||||||
Test that nginx-proxy-tester can build successfully
|
Test that nginx-proxy-tester can build successfully
|
||||||
"""
|
"""
|
||||||
import pathlib
|
|
||||||
import re
|
|
||||||
|
|
||||||
import docker
|
|
||||||
import pytest
|
import pytest
|
||||||
|
import docker
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
|
||||||
client = docker.from_env()
|
client = docker.from_env()
|
||||||
|
|
||||||
@pytest.fixture(scope = "session")
|
@pytest.fixture(scope = "session")
|
||||||
def docker_build(request):
|
def docker_build(request):
|
||||||
# Define Dockerfile path
|
# Define Dockerfile path
|
||||||
current_file_path = pathlib.Path(__file__)
|
dockerfile_path = os.path.join(os.path.dirname(__file__), "requirements/")
|
||||||
dockerfile_path = current_file_path.parent.parent.joinpath("requirements")
|
|
||||||
dockerfile_name = "Dockerfile-nginx-proxy-tester"
|
dockerfile_name = "Dockerfile-nginx-proxy-tester"
|
||||||
|
|
||||||
# Build the Docker image
|
# Build the Docker image
|
||||||
image, logs = client.images.build(
|
image, logs = client.images.build(
|
||||||
path = dockerfile_path.as_posix(),
|
path = dockerfile_path,
|
||||||
dockerfile = dockerfile_name,
|
dockerfile = dockerfile_name,
|
||||||
rm = True, # Remove intermediate containers
|
rm = True, # Remove intermediate containers
|
||||||
tag = "nginx-proxy-tester-ci", # Tag for the built image
|
tag = "nginx-proxy-tester-ci", # Tag for the built image
|
|
@ -2,6 +2,6 @@ import re
|
||||||
|
|
||||||
|
|
||||||
def test_custom_error_page(docker_compose, nginxproxy):
|
def test_custom_error_page(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("http://unknown.nginx-proxy.tld")
|
r = nginxproxy.get("http://unknown.nginx-proxy.tld", expected_status_code=503)
|
||||||
assert r.status_code == 503
|
assert r.status_code == 503
|
||||||
assert re.search(r"Damn, there's some maintenance in progress.", r.text)
|
assert re.search(r"Damn, there's some maintenance in progress.", r.text)
|
||||||
|
|
|
@ -1,5 +1,32 @@
|
||||||
services:
|
|
||||||
nginx-proxy:
|
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
nginx_conf:
|
||||||
- ${PYTEST_MODULE_PATH}/50x.html:/usr/share/nginx/html/errors/50x.html:ro
|
|
||||||
|
|
||||||
|
services:
|
||||||
|
sut:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
|
volumes:
|
||||||
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
|
- &customErrorPage ./50x.html:/usr/share/nginx/html/errors/50x.html:ro
|
||||||
|
|
||||||
|
sut-dockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
- *customErrorPage
|
||||||
|
|
||||||
|
sut-nginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
- *customErrorPage
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
||||||
|
|
|
@ -1,8 +1,3 @@
|
||||||
def test_custom_default_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
|
|
||||||
r = nginxproxy.get("http://nginx-proxy/")
|
|
||||||
assert r.status_code == 503
|
|
||||||
assert "X-test" not in r.headers
|
|
||||||
|
|
||||||
def test_custom_default_conf_applies_to_web1(docker_compose, nginxproxy):
|
def test_custom_default_conf_applies_to_web1(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("http://web1.nginx-proxy.example/port")
|
r = nginxproxy.get("http://web1.nginx-proxy.example/port")
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
@ -17,10 +12,14 @@ def test_custom_default_conf_applies_to_web2(docker_compose, nginxproxy):
|
||||||
assert "X-test" in r.headers
|
assert "X-test" in r.headers
|
||||||
assert "f00" == r.headers["X-test"]
|
assert "f00" == r.headers["X-test"]
|
||||||
|
|
||||||
|
|
||||||
def test_custom_default_conf_is_overriden_for_web3(docker_compose, nginxproxy):
|
def test_custom_default_conf_is_overriden_for_web3(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("http://web3.nginx-proxy.example/port")
|
r = nginxproxy.get("http://web3.nginx-proxy.example/port")
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert r.text == "answer from port 83\n"
|
assert r.text == "answer from port 83\n"
|
||||||
assert "X-test" in r.headers
|
assert "X-test" in r.headers
|
||||||
assert "bar" == r.headers["X-test"]
|
assert "bar" == r.headers["X-test"]
|
||||||
|
|
||||||
|
def test_custom_default_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
|
||||||
|
r = nginxproxy.get("http://nginx-proxy/", expected_status_code=503)
|
||||||
|
assert r.status_code == 503
|
||||||
|
assert "X-test" not in r.headers
|
|
@ -1,16 +1,45 @@
|
||||||
|
volumes:
|
||||||
|
nginx_conf:
|
||||||
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
nginx-proxy:
|
nginx-proxy:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
- ${PYTEST_MODULE_PATH}/my_custom_proxy_settings_f00.conf:/etc/nginx/vhost.d/default_location:ro
|
- &defaultLocation ./my_custom_proxy_settings_f00.conf:/etc/nginx/vhost.d/default_location:ro
|
||||||
- ${PYTEST_MODULE_PATH}/my_custom_proxy_settings_bar.conf:/etc/nginx/vhost.d/web3.nginx-proxy.example_location:ro
|
- &vhostLocation ./my_custom_proxy_settings_bar.conf:/etc/nginx/vhost.d/web3.nginx-proxy.example_location:ro
|
||||||
|
|
||||||
|
nginx-proxy-dockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
- *defaultLocation
|
||||||
|
- *vhostLocation
|
||||||
|
|
||||||
|
nginx-proxy-nginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
- *defaultLocation
|
||||||
|
- *vhostLocation
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
||||||
|
|
||||||
web1:
|
web1:
|
||||||
image: web
|
image: web
|
||||||
expose:
|
expose:
|
||||||
- "81"
|
- "81"
|
||||||
environment:
|
environment:
|
||||||
WEB_PORTS: "81"
|
WEB_PORTS: 81
|
||||||
VIRTUAL_HOST: web1.nginx-proxy.example
|
VIRTUAL_HOST: web1.nginx-proxy.example
|
||||||
|
|
||||||
web2:
|
web2:
|
||||||
|
@ -18,7 +47,7 @@ services:
|
||||||
expose:
|
expose:
|
||||||
- "82"
|
- "82"
|
||||||
environment:
|
environment:
|
||||||
WEB_PORTS: "82"
|
WEB_PORTS: 82
|
||||||
VIRTUAL_HOST: web2.nginx-proxy.example
|
VIRTUAL_HOST: web2.nginx-proxy.example
|
||||||
|
|
||||||
web3:
|
web3:
|
||||||
|
@ -26,5 +55,5 @@ services:
|
||||||
expose:
|
expose:
|
||||||
- "83"
|
- "83"
|
||||||
environment:
|
environment:
|
||||||
WEB_PORTS: "83"
|
WEB_PORTS: 83
|
||||||
VIRTUAL_HOST: web3.nginx-proxy.example
|
VIRTUAL_HOST: web3.nginx-proxy.example
|
||||||
|
|
|
@ -1,8 +1,3 @@
|
||||||
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
|
|
||||||
r = nginxproxy.get("http://nginx-proxy/")
|
|
||||||
assert r.status_code == 503
|
|
||||||
assert "X-test" not in r.headers
|
|
||||||
|
|
||||||
def test_custom_conf_applies_to_web1(docker_compose, nginxproxy):
|
def test_custom_conf_applies_to_web1(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("http://web1.nginx-proxy.example/port")
|
r = nginxproxy.get("http://web1.nginx-proxy.example/port")
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
@ -16,3 +11,8 @@ def test_custom_conf_applies_to_web2(docker_compose, nginxproxy):
|
||||||
assert r.text == "answer from port 82\n"
|
assert r.text == "answer from port 82\n"
|
||||||
assert "X-test" in r.headers
|
assert "X-test" in r.headers
|
||||||
assert "f00" == r.headers["X-test"]
|
assert "f00" == r.headers["X-test"]
|
||||||
|
|
||||||
|
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
|
||||||
|
r = nginxproxy.get("http://nginx-proxy/", expected_status_code=503)
|
||||||
|
assert r.status_code == 503
|
||||||
|
assert "X-test" not in r.headers
|
|
@ -1,15 +1,42 @@
|
||||||
|
volumes:
|
||||||
|
nginx_conf:
|
||||||
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
nginx-proxy:
|
nginx-proxy:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
- ${PYTEST_MODULE_PATH}/my_custom_proxy_settings_f00.conf:/etc/nginx/proxy.conf:ro
|
- &defaultConf ./my_custom_proxy_settings_f00.conf:/etc/nginx/proxy.conf:ro
|
||||||
|
|
||||||
|
nginx-proxy-dockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
- *defaultConf
|
||||||
|
|
||||||
|
nginx-proxy-nginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
- *defaultConf
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
||||||
|
|
||||||
web1:
|
web1:
|
||||||
image: web
|
image: web
|
||||||
expose:
|
expose:
|
||||||
- "81"
|
- "81"
|
||||||
environment:
|
environment:
|
||||||
WEB_PORTS: "81"
|
WEB_PORTS: 81
|
||||||
VIRTUAL_HOST: web1.nginx-proxy.example
|
VIRTUAL_HOST: web1.nginx-proxy.example
|
||||||
|
|
||||||
web2:
|
web2:
|
||||||
|
@ -17,5 +44,5 @@ services:
|
||||||
expose:
|
expose:
|
||||||
- "82"
|
- "82"
|
||||||
environment:
|
environment:
|
||||||
WEB_PORTS: "82"
|
WEB_PORTS: 82
|
||||||
VIRTUAL_HOST: web2.nginx-proxy.example
|
VIRTUAL_HOST: web2.nginx-proxy.example
|
||||||
|
|
|
@ -1,8 +1,3 @@
|
||||||
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
|
|
||||||
r = nginxproxy.get("http://nginx-proxy/")
|
|
||||||
assert r.status_code == 503
|
|
||||||
assert "X-test" not in r.headers
|
|
||||||
|
|
||||||
def test_custom_conf_applies_to_web1(docker_compose, nginxproxy):
|
def test_custom_conf_applies_to_web1(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("http://web1.nginx-proxy.example/port")
|
r = nginxproxy.get("http://web1.nginx-proxy.example/port")
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
@ -23,5 +18,10 @@ def test_custom_conf_does_not_apply_to_web2(docker_compose, nginxproxy):
|
||||||
assert r.text == "answer from port 82\n"
|
assert r.text == "answer from port 82\n"
|
||||||
assert "X-test" not in r.headers
|
assert "X-test" not in r.headers
|
||||||
|
|
||||||
|
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
|
||||||
|
r = nginxproxy.get("http://nginx-proxy/", expected_status_code=503)
|
||||||
|
assert r.status_code == 503
|
||||||
|
assert "X-test" not in r.headers
|
||||||
|
|
||||||
def test_custom_block_is_present_in_nginx_generated_conf(docker_compose, nginxproxy):
|
def test_custom_block_is_present_in_nginx_generated_conf(docker_compose, nginxproxy):
|
||||||
assert b"include /etc/nginx/vhost.d/web1.nginx-proxy.example_location;" in nginxproxy.get_conf()
|
assert b"include /etc/nginx/vhost.d/web1.nginx-proxy.example_location;" in nginxproxy.get_conf()
|
|
@ -1,16 +1,45 @@
|
||||||
|
volumes:
|
||||||
|
nginx_conf:
|
||||||
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
nginx-proxy:
|
nginx-proxy:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
- ${PYTEST_MODULE_PATH}/my_custom_proxy_settings_f00.conf:/etc/nginx/vhost.d/web1.nginx-proxy.example_location:ro
|
- &vhostLocationConf ./my_custom_proxy_settings_f00.conf:/etc/nginx/vhost.d/web1.nginx-proxy.example_location:ro
|
||||||
- ${PYTEST_MODULE_PATH}/my_custom_proxy_settings_bar.conf:/etc/nginx/vhost.d/561032515ede3ab3a015edfb244608b72409c430_location:ro
|
- ®exLocationConf ./my_custom_proxy_settings_bar.conf:/etc/nginx/vhost.d/561032515ede3ab3a015edfb244608b72409c430_location:ro
|
||||||
|
|
||||||
|
nginx-proxy-dockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
- *vhostLocationConf
|
||||||
|
- *regexLocationConf
|
||||||
|
|
||||||
|
nginx-proxy-nginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
- *vhostLocationConf
|
||||||
|
- *regexLocationConf
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
||||||
|
|
||||||
web1:
|
web1:
|
||||||
image: web
|
image: web
|
||||||
expose:
|
expose:
|
||||||
- "81"
|
- "81"
|
||||||
environment:
|
environment:
|
||||||
WEB_PORTS: "81"
|
WEB_PORTS: 81
|
||||||
VIRTUAL_HOST: web1.nginx-proxy.example
|
VIRTUAL_HOST: web1.nginx-proxy.example
|
||||||
|
|
||||||
web2:
|
web2:
|
||||||
|
@ -18,7 +47,7 @@ services:
|
||||||
expose:
|
expose:
|
||||||
- "82"
|
- "82"
|
||||||
environment:
|
environment:
|
||||||
WEB_PORTS: "82"
|
WEB_PORTS: 82
|
||||||
VIRTUAL_HOST: web2.nginx-proxy.example
|
VIRTUAL_HOST: web2.nginx-proxy.example
|
||||||
|
|
||||||
regex:
|
regex:
|
||||||
|
@ -26,5 +55,5 @@ services:
|
||||||
expose:
|
expose:
|
||||||
- "83"
|
- "83"
|
||||||
environment:
|
environment:
|
||||||
WEB_PORTS: "83"
|
WEB_PORTS: 83
|
||||||
VIRTUAL_HOST: ~^regex.*\.nginx-proxy\.example$
|
VIRTUAL_HOST: ~^regex.*\.nginx-proxy\.example$
|
||||||
|
|
|
@ -1,8 +1,3 @@
|
||||||
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
|
|
||||||
r = nginxproxy.get("http://nginx-proxy/")
|
|
||||||
assert r.status_code == 503
|
|
||||||
assert "X-test" not in r.headers
|
|
||||||
|
|
||||||
def test_custom_conf_applies_to_web1(docker_compose, nginxproxy):
|
def test_custom_conf_applies_to_web1(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("http://web1.nginx-proxy.example/port")
|
r = nginxproxy.get("http://web1.nginx-proxy.example/port")
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
@ -22,3 +17,8 @@ def test_custom_conf_does_not_apply_to_web2(docker_compose, nginxproxy):
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert r.text == "answer from port 82\n"
|
assert r.text == "answer from port 82\n"
|
||||||
assert "X-test" not in r.headers
|
assert "X-test" not in r.headers
|
||||||
|
|
||||||
|
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
|
||||||
|
r = nginxproxy.get("http://nginx-proxy/", expected_status_code=503)
|
||||||
|
assert r.status_code == 503
|
||||||
|
assert "X-test" not in r.headers
|
|
@ -1,16 +1,45 @@
|
||||||
|
volumes:
|
||||||
|
nginx_conf:
|
||||||
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
nginx-proxy:
|
nginx-proxy:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
- ${PYTEST_MODULE_PATH}/my_custom_proxy_settings_f00.conf:/etc/nginx/vhost.d/web1.nginx-proxy.example:ro
|
- &vhostConf ./my_custom_proxy_settings_f00.conf:/etc/nginx/vhost.d/web1.nginx-proxy.example:ro
|
||||||
- ${PYTEST_MODULE_PATH}/my_custom_proxy_settings_bar.conf:/etc/nginx/vhost.d/561032515ede3ab3a015edfb244608b72409c430:ro
|
- ®exConf ./my_custom_proxy_settings_bar.conf:/etc/nginx/vhost.d/561032515ede3ab3a015edfb244608b72409c430:ro
|
||||||
|
|
||||||
|
nginx-proxy-dockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
- *vhostConf
|
||||||
|
- *regexConf
|
||||||
|
|
||||||
|
nginx-proxy-nginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
- *vhostConf
|
||||||
|
- *regexConf
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
||||||
|
|
||||||
web1:
|
web1:
|
||||||
image: web
|
image: web
|
||||||
expose:
|
expose:
|
||||||
- "81"
|
- "81"
|
||||||
environment:
|
environment:
|
||||||
WEB_PORTS: "81"
|
WEB_PORTS: 81
|
||||||
VIRTUAL_HOST: web1.nginx-proxy.example
|
VIRTUAL_HOST: web1.nginx-proxy.example
|
||||||
|
|
||||||
web2:
|
web2:
|
||||||
|
@ -18,7 +47,7 @@ services:
|
||||||
expose:
|
expose:
|
||||||
- "82"
|
- "82"
|
||||||
environment:
|
environment:
|
||||||
WEB_PORTS: "82"
|
WEB_PORTS: 82
|
||||||
VIRTUAL_HOST: web2.nginx-proxy.example
|
VIRTUAL_HOST: web2.nginx-proxy.example
|
||||||
|
|
||||||
regex:
|
regex:
|
||||||
|
@ -26,5 +55,5 @@ services:
|
||||||
expose:
|
expose:
|
||||||
- "83"
|
- "83"
|
||||||
environment:
|
environment:
|
||||||
WEB_PORTS: "83"
|
WEB_PORTS: 83
|
||||||
VIRTUAL_HOST: ~^regex.*\.nginx-proxy\.example$
|
VIRTUAL_HOST: ~^regex.*\.nginx-proxy\.example$
|
||||||
|
|
|
@ -1,8 +1,3 @@
|
||||||
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
|
|
||||||
r = nginxproxy.get("http://nginx-proxy/")
|
|
||||||
assert r.status_code == 503
|
|
||||||
assert "X-test" not in r.headers
|
|
||||||
|
|
||||||
def test_custom_conf_applies_to_web1(docker_compose, nginxproxy):
|
def test_custom_conf_applies_to_web1(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("http://web1.nginx-proxy.example/port")
|
r = nginxproxy.get("http://web1.nginx-proxy.example/port")
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
@ -16,3 +11,8 @@ def test_custom_conf_applies_to_web2(docker_compose, nginxproxy):
|
||||||
assert r.text == "answer from port 82\n"
|
assert r.text == "answer from port 82\n"
|
||||||
assert "X-test" in r.headers
|
assert "X-test" in r.headers
|
||||||
assert "f00" == r.headers["X-test"]
|
assert "f00" == r.headers["X-test"]
|
||||||
|
|
||||||
|
def test_custom_conf_does_not_apply_to_unknown_vhost(docker_compose, nginxproxy):
|
||||||
|
r = nginxproxy.get("http://nginx-proxy/", expected_status_code=503)
|
||||||
|
assert r.status_code == 503
|
||||||
|
assert "X-test" not in r.headers
|
|
@ -1,15 +1,42 @@
|
||||||
|
volumes:
|
||||||
|
nginx_conf:
|
||||||
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
nginx-proxy:
|
nginx-proxy:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
- ${PYTEST_MODULE_PATH}/my_custom_proxy_settings_f00.conf:/etc/nginx/conf.d/my_custom_proxy_settings_f00.conf:ro
|
- &proxyConf ./my_custom_proxy_settings_f00.conf:/etc/nginx/conf.d/my_custom_proxy_settings_f00.conf:ro
|
||||||
|
|
||||||
|
nginx-proxy-dockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
- *proxyConf
|
||||||
|
|
||||||
|
nginx-proxy-nginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
- *proxyConf
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
||||||
|
|
||||||
web1:
|
web1:
|
||||||
image: web
|
image: web
|
||||||
expose:
|
expose:
|
||||||
- "81"
|
- "81"
|
||||||
environment:
|
environment:
|
||||||
WEB_PORTS: "81"
|
WEB_PORTS: 81
|
||||||
VIRTUAL_HOST: web1.nginx-proxy.example
|
VIRTUAL_HOST: web1.nginx-proxy.example
|
||||||
|
|
||||||
web2:
|
web2:
|
||||||
|
@ -17,5 +44,5 @@ services:
|
||||||
expose:
|
expose:
|
||||||
- "82"
|
- "82"
|
||||||
environment:
|
environment:
|
||||||
WEB_PORTS: "82"
|
WEB_PORTS: 82
|
||||||
VIRTUAL_HOST: web2.nginx-proxy.example
|
VIRTUAL_HOST: web2.nginx-proxy.example
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
services:
|
|
||||||
debug_disabled1:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "81"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "81"
|
|
||||||
VIRTUAL_HOST: disabled1.debug.nginx-proxy.example
|
|
||||||
|
|
||||||
debug_disabled2:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "82"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "82"
|
|
||||||
VIRTUAL_HOST: disabled2.debug.nginx-proxy.example
|
|
||||||
|
|
||||||
|
|
||||||
debug_enabled:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "83"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "83"
|
|
||||||
VIRTUAL_HOST: enabled.debug.nginx-proxy.example
|
|
||||||
labels:
|
|
||||||
com.github.nginx-proxy.nginx-proxy.debug-endpoint: "true"
|
|
|
@ -1,8 +1,6 @@
|
||||||
import json
|
import json
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
def test_debug_endpoint_is_enabled_globally(docker_compose, nginxproxy):
|
def test_debug_endpoint_is_enabled_globally(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("http://enabled.debug.nginx-proxy.example/nginx-proxy-debug")
|
r = nginxproxy.get("http://enabled.debug.nginx-proxy.example/nginx-proxy-debug")
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
@ -44,5 +42,5 @@ def test_debug_endpoint_hostname_replaced_by_warning_if_regexp(docker_compose, n
|
||||||
|
|
||||||
|
|
||||||
def test_debug_endpoint_is_disabled_per_container(docker_compose, nginxproxy):
|
def test_debug_endpoint_is_disabled_per_container(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("http://disabled.debug.nginx-proxy.example/nginx-proxy-debug")
|
r = nginxproxy.get("http://disabled.debug.nginx-proxy.example/nginx-proxy-debug", expected_status_code=404)
|
||||||
assert r.status_code == 404
|
assert r.status_code == 404
|
|
@ -1,14 +1,42 @@
|
||||||
|
volumes:
|
||||||
|
nginx_conf:
|
||||||
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
nginx-proxy:
|
nginx-proxy:
|
||||||
environment:
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
|
volumes:
|
||||||
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
|
environment: &nginxProxyEnv
|
||||||
DEBUG_ENDPOINT: "true"
|
DEBUG_ENDPOINT: "true"
|
||||||
|
|
||||||
|
nginx-proxy-dockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
environment: *nginxProxyEnv
|
||||||
|
|
||||||
|
nginx-proxy-nginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
||||||
|
|
||||||
debug_enabled:
|
debug_enabled:
|
||||||
image: web
|
image: web
|
||||||
expose:
|
expose:
|
||||||
- "81"
|
- "81"
|
||||||
environment:
|
environment:
|
||||||
WEB_PORTS: "81"
|
WEB_PORTS: 81
|
||||||
VIRTUAL_HOST: enabled.debug.nginx-proxy.example
|
VIRTUAL_HOST: enabled.debug.nginx-proxy.example
|
||||||
|
|
||||||
debug_stripped:
|
debug_stripped:
|
||||||
|
@ -16,7 +44,7 @@ services:
|
||||||
expose:
|
expose:
|
||||||
- "82"
|
- "82"
|
||||||
environment:
|
environment:
|
||||||
WEB_PORTS: "82"
|
WEB_PORTS: 82
|
||||||
VIRTUAL_HOST_MULTIPORTS: |-
|
VIRTUAL_HOST_MULTIPORTS: |-
|
||||||
stripped.debug.nginx-proxy.example:
|
stripped.debug.nginx-proxy.example:
|
||||||
"/1":
|
"/1":
|
||||||
|
@ -45,7 +73,7 @@ services:
|
||||||
expose:
|
expose:
|
||||||
- "84"
|
- "84"
|
||||||
environment:
|
environment:
|
||||||
WEB_PORTS: "84"
|
WEB_PORTS: 84
|
||||||
VIRTUAL_HOST: ~^regexp.*\.debug.nginx-proxy.example
|
VIRTUAL_HOST: ~^regexp.*\.debug.nginx-proxy.example
|
||||||
|
|
||||||
debug_disabled:
|
debug_disabled:
|
||||||
|
@ -53,7 +81,7 @@ services:
|
||||||
expose:
|
expose:
|
||||||
- "83"
|
- "83"
|
||||||
environment:
|
environment:
|
||||||
WEB_PORTS: "83"
|
WEB_PORTS: 83
|
||||||
VIRTUAL_HOST: disabled.debug.nginx-proxy.example
|
VIRTUAL_HOST: disabled.debug.nginx-proxy.example
|
||||||
labels:
|
labels:
|
||||||
com.github.nginx-proxy.nginx-proxy.debug-endpoint: "false"
|
com.github.nginx-proxy.nginx-proxy.debug-endpoint: "false"
|
|
@ -1,12 +1,10 @@
|
||||||
import json
|
import json
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
def test_debug_endpoint_is_disabled_globally(docker_compose, nginxproxy):
|
def test_debug_endpoint_is_disabled_globally(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("http://disabled1.debug.nginx-proxy.example/nginx-proxy-debug")
|
r = nginxproxy.get("http://disabled1.debug.nginx-proxy.example/nginx-proxy-debug", expected_status_code=404)
|
||||||
assert r.status_code == 404
|
assert r.status_code == 404
|
||||||
r = nginxproxy.get("http://disabled2.debug.nginx-proxy.example/nginx-proxy-debug")
|
r = nginxproxy.get("http://disabled2.debug.nginx-proxy.example/nginx-proxy-debug", expected_status_code=404)
|
||||||
assert r.status_code == 404
|
assert r.status_code == 404
|
||||||
|
|
||||||
|
|
55
test/test_debug_endpoint/test_per_container.yml
Normal file
55
test/test_debug_endpoint/test_per_container.yml
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
volumes:
|
||||||
|
nginx_conf:
|
||||||
|
|
||||||
|
|
||||||
|
services:
|
||||||
|
nginx-proxy:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
|
volumes:
|
||||||
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
|
|
||||||
|
nginx-proxy-dockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
|
||||||
|
nginx-proxy-nginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
||||||
|
|
||||||
|
debug_disabled1:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "81"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: 81
|
||||||
|
VIRTUAL_HOST: disabled1.debug.nginx-proxy.example
|
||||||
|
|
||||||
|
debug_disabled2:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "82"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: 82
|
||||||
|
VIRTUAL_HOST: disabled2.debug.nginx-proxy.example
|
||||||
|
|
||||||
|
debug_enabled:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "83"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: 83
|
||||||
|
VIRTUAL_HOST: enabled.debug.nginx-proxy.example
|
||||||
|
labels:
|
||||||
|
com.github.nginx-proxy.nginx-proxy.debug-endpoint: "true"
|
40
test/test_default-host.yml
Normal file
40
test/test_default-host.yml
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
volumes:
|
||||||
|
nginx_conf:
|
||||||
|
|
||||||
|
|
||||||
|
services:
|
||||||
|
web1:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "81"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: 81
|
||||||
|
VIRTUAL_HOST: web1.tld
|
||||||
|
|
||||||
|
sut:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
|
volumes:
|
||||||
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
|
environment: &nginxProxyEnv
|
||||||
|
DEFAULT_HOST: web1.tld
|
||||||
|
|
||||||
|
sutdockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
environment: *nginxProxyEnv
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
|
||||||
|
sutnginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
|
@ -1,12 +0,0 @@
|
||||||
services:
|
|
||||||
nginx-proxy:
|
|
||||||
environment:
|
|
||||||
DEFAULT_HOST: web1.tld
|
|
||||||
|
|
||||||
web1:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "81"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "81"
|
|
||||||
VIRTUAL_HOST: web1.tld
|
|
|
@ -1,22 +0,0 @@
|
||||||
services:
|
|
||||||
nginx-proxy:
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/f00.sock:ro
|
|
||||||
environment:
|
|
||||||
DOCKER_HOST: unix:///f00.sock
|
|
||||||
|
|
||||||
web1:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "81"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "81"
|
|
||||||
VIRTUAL_HOST: web1.nginx-proxy.tld
|
|
||||||
|
|
||||||
web2:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "82"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "82"
|
|
||||||
VIRTUAL_HOST: web2.nginx-proxy.tld
|
|
|
@ -1,7 +1,3 @@
|
||||||
def test_unknown_virtual_host(docker_compose, nginxproxy):
|
|
||||||
r = nginxproxy.get("http://nginx-proxy/port")
|
|
||||||
assert r.status_code == 503
|
|
||||||
|
|
||||||
def test_forwards_to_web1(docker_compose, nginxproxy):
|
def test_forwards_to_web1(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("http://web1.nginx-proxy.tld/port")
|
r = nginxproxy.get("http://web1.nginx-proxy.tld/port")
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
@ -11,3 +7,7 @@ def test_forwards_to_web2(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("http://web2.nginx-proxy.tld/port")
|
r = nginxproxy.get("http://web2.nginx-proxy.tld/port")
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert r.text == "answer from port 82\n"
|
assert r.text == "answer from port 82\n"
|
||||||
|
|
||||||
|
def test_unknown_virtual_host(docker_compose, nginxproxy):
|
||||||
|
r = nginxproxy.get("http://nginx-proxy/port", expected_status_code=503)
|
||||||
|
assert r.status_code == 503
|
48
test/test_docker_unix_socket.yml
Normal file
48
test/test_docker_unix_socket.yml
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
volumes:
|
||||||
|
nginx_conf:
|
||||||
|
|
||||||
|
|
||||||
|
services:
|
||||||
|
web1:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "81"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: 81
|
||||||
|
VIRTUAL_HOST: web1.nginx-proxy.tld
|
||||||
|
|
||||||
|
web2:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "82"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: 82
|
||||||
|
VIRTUAL_HOST: web2.nginx-proxy.tld
|
||||||
|
|
||||||
|
sut:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
|
volumes:
|
||||||
|
- &dockerSocket /var/run/docker.sock:/f00.sock:ro
|
||||||
|
environment: &nginxProxyEnv
|
||||||
|
DOCKER_HOST: unix:///f00.sock
|
||||||
|
|
||||||
|
sutdockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
environment: *nginxProxyEnv
|
||||||
|
|
||||||
|
sutnginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
1
test/test_dockergen/.gitignore
vendored
Normal file
1
test/test_dockergen/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
nginx.tmpl
|
|
@ -1,12 +1,10 @@
|
||||||
import docker
|
import os
|
||||||
import pytest
|
import pytest
|
||||||
from packaging.version import Version
|
|
||||||
|
|
||||||
|
|
||||||
raw_version = docker.from_env().version()["Version"]
|
|
||||||
pytestmark = pytest.mark.skipif(
|
pytestmark = pytest.mark.skipif(
|
||||||
Version(raw_version) < Version("1.13"),
|
True,
|
||||||
reason="Docker compose syntax v3 requires docker engine v1.13 or later (got {raw_version})"
|
reason="This test intefers with the other tests, and might no longer be needed."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,21 @@
|
||||||
volumes:
|
volumes:
|
||||||
nginx_conf:
|
nginx_conf_dockergen:
|
||||||
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
nginx-proxy-nginx:
|
nginx:
|
||||||
image: nginx
|
image: nginx
|
||||||
container_name: nginx
|
container_name: nginx
|
||||||
volumes:
|
volumes:
|
||||||
- nginx_conf:/etc/nginx/conf.d:ro
|
- nginx_conf_dockergen:/etc/nginx/conf.d:ro
|
||||||
ports:
|
|
||||||
- "80:80"
|
|
||||||
- "443:443"
|
|
||||||
|
|
||||||
nginx-proxy-dockergen:
|
dockergen:
|
||||||
image: nginxproxy/docker-gen
|
image: nginxproxy/docker-gen
|
||||||
command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
|
command: -notify-sighup nginx -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
- /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
- ../../nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl
|
- ../../nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl
|
||||||
- nginx_conf:/etc/nginx/conf.d
|
- nginx_conf_dockergen:/etc/nginx/conf.d
|
||||||
|
|
||||||
web:
|
web:
|
||||||
image: web
|
image: web
|
||||||
|
@ -26,5 +23,5 @@ services:
|
||||||
expose:
|
expose:
|
||||||
- "80"
|
- "80"
|
||||||
environment:
|
environment:
|
||||||
WEB_PORTS: "80"
|
WEB_PORTS: 80
|
||||||
VIRTUAL_HOST: whoami.nginx.container.docker
|
VIRTUAL_HOST: whoami.nginx.container.docker
|
|
@ -1,15 +1,23 @@
|
||||||
def test_nohttp_missing_cert_disabled(docker_compose, nginxproxy):
|
|
||||||
r = nginxproxy.get("http://nohttp-missing-cert-disabled.nginx-proxy.tld/", allow_redirects=False)
|
|
||||||
assert r.status_code == 503
|
|
||||||
|
|
||||||
def test_nohttp_missing_cert_enabled(docker_compose, nginxproxy):
|
def test_nohttp_missing_cert_enabled(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("http://nohttp-missing-cert-enabled.nginx-proxy.tld/", allow_redirects=False)
|
r = nginxproxy.get("http://nohttp-missing-cert-enabled.nginx-proxy.tld/", allow_redirects=False)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
|
||||||
def test_redirect_missing_cert_disabled(docker_compose, nginxproxy):
|
def test_nohttp_missing_cert_disabled(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("http://redirect-missing-cert-disabled.nginx-proxy.tld/", allow_redirects=False)
|
r = nginxproxy.get(
|
||||||
assert r.status_code == 301
|
"http://nohttp-missing-cert-disabled.nginx-proxy.tld/",
|
||||||
|
allow_redirects=False,
|
||||||
|
expected_status_code=503
|
||||||
|
)
|
||||||
|
assert r.status_code == 503
|
||||||
|
|
||||||
def test_redirect_missing_cert_enabled(docker_compose, nginxproxy):
|
def test_redirect_missing_cert_enabled(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("http://redirect-missing-cert-enabled.nginx-proxy.tld/", allow_redirects=False)
|
r = nginxproxy.get("http://redirect-missing-cert-enabled.nginx-proxy.tld/", allow_redirects=False)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
|
||||||
|
def test_redirect_missing_cert_disabled(docker_compose, nginxproxy):
|
||||||
|
r = nginxproxy.get(
|
||||||
|
"http://redirect-missing-cert-disabled.nginx-proxy.tld/",
|
||||||
|
allow_redirects=False,
|
||||||
|
expected_status_code=301
|
||||||
|
)
|
||||||
|
assert r.status_code == 301
|
|
@ -1,8 +1,39 @@
|
||||||
|
volumes:
|
||||||
|
nginx_conf:
|
||||||
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
nginx-proxy:
|
sut:
|
||||||
environment:
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
|
volumes:
|
||||||
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
|
- &certs ./withdefault.certs:/etc/nginx/certs:ro
|
||||||
|
environment: &nginxProxyEnv
|
||||||
ENABLE_HTTP_ON_MISSING_CERT: "false"
|
ENABLE_HTTP_ON_MISSING_CERT: "false"
|
||||||
|
|
||||||
|
sutdockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
environment: *nginxProxyEnv
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
- *certs
|
||||||
|
|
||||||
|
sutnginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
- *certs
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
||||||
|
|
||||||
nohttp-missing-cert-disabled:
|
nohttp-missing-cert-disabled:
|
||||||
image: web
|
image: web
|
||||||
expose:
|
expose:
|
|
@ -7,7 +7,7 @@ import pytest
|
||||||
from docker.errors import NotFound
|
from docker.errors import NotFound
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture()
|
||||||
def web1(docker_compose):
|
def web1(docker_compose):
|
||||||
"""
|
"""
|
||||||
pytest fixture creating a web container with `VIRTUAL_HOST=web1.nginx-proxy` listening on port 81.
|
pytest fixture creating a web container with `VIRTUAL_HOST=web1.nginx-proxy` listening on port 81.
|
||||||
|
@ -22,7 +22,7 @@ def web1(docker_compose):
|
||||||
},
|
},
|
||||||
ports={"81/tcp": None}
|
ports={"81/tcp": None}
|
||||||
)
|
)
|
||||||
docker_compose.networks.get("test_events-net").connect(container)
|
docker_compose.networks.get("nginx-proxy-test-events").connect(container)
|
||||||
sleep(2) # give it some time to initialize and for docker-gen to detect it
|
sleep(2) # give it some time to initialize and for docker-gen to detect it
|
||||||
yield container
|
yield container
|
||||||
try:
|
try:
|
||||||
|
@ -30,7 +30,7 @@ def web1(docker_compose):
|
||||||
except NotFound:
|
except NotFound:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture()
|
||||||
def web2(docker_compose):
|
def web2(docker_compose):
|
||||||
"""
|
"""
|
||||||
pytest fixture creating a web container with `VIRTUAL_HOST=nginx-proxy`, `VIRTUAL_PATH=/web2/` and `VIRTUAL_DEST=/` listening on port 82.
|
pytest fixture creating a web container with `VIRTUAL_HOST=nginx-proxy`, `VIRTUAL_PATH=/web2/` and `VIRTUAL_DEST=/` listening on port 82.
|
||||||
|
@ -47,7 +47,7 @@ def web2(docker_compose):
|
||||||
},
|
},
|
||||||
ports={"82/tcp": None}
|
ports={"82/tcp": None}
|
||||||
)
|
)
|
||||||
docker_compose.networks.get("test_events-net").connect(container)
|
docker_compose.networks.get("nginx-proxy-test-events").connect(container)
|
||||||
sleep(2) # give it some time to initialize and for docker-gen to detect it
|
sleep(2) # give it some time to initialize and for docker-gen to detect it
|
||||||
yield container
|
yield container
|
||||||
try:
|
try:
|
||||||
|
@ -56,7 +56,7 @@ def web2(docker_compose):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_nginx_proxy_behavior_when_alone(docker_compose, nginxproxy):
|
def test_nginx_proxy_behavior_when_alone(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("http://nginx-proxy/")
|
r = nginxproxy.get("http://nginx-proxy/", expected_status_code=503)
|
||||||
assert r.status_code == 503
|
assert r.status_code == 503
|
||||||
|
|
||||||
|
|
||||||
|
@ -67,18 +67,18 @@ def test_new_container_is_detected_vhost(web1, nginxproxy):
|
||||||
|
|
||||||
web1.remove(force=True)
|
web1.remove(force=True)
|
||||||
sleep(2)
|
sleep(2)
|
||||||
r = nginxproxy.get("http://web1.nginx-proxy/port")
|
r = nginxproxy.get("http://web1.nginx-proxy/port", expected_status_code=503)
|
||||||
assert r.status_code == 503
|
assert r.status_code == 503
|
||||||
|
|
||||||
def test_new_container_is_detected_vpath(web2, nginxproxy):
|
def test_new_container_is_detected_vpath(web2, nginxproxy):
|
||||||
r = nginxproxy.get("http://nginx-proxy/web2/port")
|
r = nginxproxy.get("http://nginx-proxy/web2/port")
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert "answer from port 82\n" == r.text
|
assert "answer from port 82\n" == r.text
|
||||||
r = nginxproxy.get("http://nginx-proxy/port")
|
r = nginxproxy.get("http://nginx-proxy/port", expected_status_code=[404, 503])
|
||||||
assert r.status_code in [404, 503]
|
assert r.status_code in [404, 503]
|
||||||
|
|
||||||
web2.remove(force=True)
|
web2.remove(force=True)
|
||||||
sleep(2)
|
sleep(2)
|
||||||
r = nginxproxy.get("http://nginx-proxy/web2/port")
|
r = nginxproxy.get("http://nginx-proxy/web2/port", expected_status_code=503)
|
||||||
assert r.status_code == 503
|
assert r.status_code == 503
|
||||||
|
|
33
test/test_events.yml
Normal file
33
test/test_events.yml
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
name: nginx-proxy-test-events
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
nginx_conf:
|
||||||
|
|
||||||
|
|
||||||
|
services:
|
||||||
|
nginxproxy:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
|
volumes:
|
||||||
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
|
|
||||||
|
nginxproxy-dockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
|
||||||
|
nginxproxy-nginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
|
@ -1,3 +0,0 @@
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
name: test_events-net
|
|
41
test/test_fallback.data/custom-fallback.yml
Normal file
41
test/test_fallback.data/custom-fallback.yml
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
volumes:
|
||||||
|
nginx_conf:
|
||||||
|
|
||||||
|
|
||||||
|
services:
|
||||||
|
sut:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
|
volumes:
|
||||||
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
|
- &customFallback ./custom-fallback.conf:/etc/nginx/conf.d/zzz-custom-fallback.conf:ro
|
||||||
|
|
||||||
|
sut-dockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
- *customFallback
|
||||||
|
|
||||||
|
sut-nginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
- *customFallback
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
||||||
|
|
||||||
|
http-only:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "83"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: "83"
|
||||||
|
VIRTUAL_HOST: http-only.nginx-proxy.test
|
||||||
|
HTTPS_METHOD: nohttps
|
|
@ -1,8 +1,35 @@
|
||||||
services:
|
|
||||||
nginx-proxy:
|
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
nginx_conf:
|
||||||
- ${PYTEST_MODULE_PATH}/test_fallback.data/nodefault.certs:/etc/nginx/certs:ro
|
|
||||||
|
|
||||||
|
services:
|
||||||
|
sut:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
|
volumes:
|
||||||
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
|
- &certs ./nodefault.certs:/etc/nginx/certs:ro
|
||||||
|
|
||||||
|
sut-dockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
- *certs
|
||||||
|
|
||||||
|
sut-nginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
- *certs
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
||||||
|
|
||||||
https-and-http:
|
https-and-http:
|
||||||
image: web
|
image: web
|
44
test/test_fallback.data/nohttp-on-app.yml
Normal file
44
test/test_fallback.data/nohttp-on-app.yml
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
volumes:
|
||||||
|
nginx_conf:
|
||||||
|
|
||||||
|
|
||||||
|
services:
|
||||||
|
sut:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
|
volumes:
|
||||||
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
|
- &certs ./withdefault.certs:/etc/nginx/certs:ro
|
||||||
|
environment: &nginxProxyEnv
|
||||||
|
HTTPS_METHOD: redirect
|
||||||
|
|
||||||
|
sut-dockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
- *certs
|
||||||
|
environment: *nginxProxyEnv
|
||||||
|
|
||||||
|
sut-nginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
- *certs
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
||||||
|
|
||||||
|
https-only:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "82"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: "82"
|
||||||
|
HTTPS_METHOD: nohttp
|
||||||
|
VIRTUAL_HOST: https-only.nginx-proxy.test
|
61
test/test_fallback.data/nohttp-with-missing-cert.yml
Normal file
61
test/test_fallback.data/nohttp-with-missing-cert.yml
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
volumes:
|
||||||
|
nginx_conf:
|
||||||
|
|
||||||
|
|
||||||
|
services:
|
||||||
|
sut:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
|
volumes:
|
||||||
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
|
- &certs ./withdefault.certs:/etc/nginx/certs:ro
|
||||||
|
environment: &nginxProxyEnv
|
||||||
|
HTTPS_METHOD: nohttp
|
||||||
|
|
||||||
|
sut-dockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
- *certs
|
||||||
|
environment: *nginxProxyEnv
|
||||||
|
|
||||||
|
sut-nginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
- *certs
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
||||||
|
|
||||||
|
https-only:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "82"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: "82"
|
||||||
|
VIRTUAL_HOST: https-only.nginx-proxy.test
|
||||||
|
|
||||||
|
missing-cert:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "84"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: "84"
|
||||||
|
VIRTUAL_HOST: missing-cert.nginx-proxy.test
|
||||||
|
|
||||||
|
missing-cert-default-untrusted:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "85"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: "85"
|
||||||
|
VIRTUAL_HOST: missing-cert.default-untrusted.nginx-proxy.test
|
||||||
|
labels:
|
||||||
|
com.github.nginx-proxy.nginx-proxy.trust-default-cert: "false"
|
43
test/test_fallback.data/nohttp.yml
Normal file
43
test/test_fallback.data/nohttp.yml
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
volumes:
|
||||||
|
nginx_conf:
|
||||||
|
|
||||||
|
|
||||||
|
services:
|
||||||
|
sut:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
|
volumes:
|
||||||
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
|
- &certs ./withdefault.certs:/etc/nginx/certs:ro
|
||||||
|
environment: &nginxProxyEnv
|
||||||
|
HTTPS_METHOD: nohttp
|
||||||
|
|
||||||
|
sut-dockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
- *certs
|
||||||
|
environment: *nginxProxyEnv
|
||||||
|
|
||||||
|
sut-nginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
- *certs
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
||||||
|
|
||||||
|
https-only:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "82"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: "82"
|
||||||
|
VIRTUAL_HOST: https-only.nginx-proxy.test
|
41
test/test_fallback.data/nohttps-on-app.yml
Normal file
41
test/test_fallback.data/nohttps-on-app.yml
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
volumes:
|
||||||
|
nginx_conf:
|
||||||
|
|
||||||
|
|
||||||
|
services:
|
||||||
|
sut:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
|
volumes:
|
||||||
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
|
environment: &nginxProxyEnv
|
||||||
|
HTTPS_METHOD: redirect
|
||||||
|
|
||||||
|
sut-dockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
environment: *nginxProxyEnv
|
||||||
|
|
||||||
|
sut-nginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
||||||
|
|
||||||
|
http-only:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "83"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: "83"
|
||||||
|
HTTPS_METHOD: nohttps
|
||||||
|
VIRTUAL_HOST: http-only.nginx-proxy.test
|
40
test/test_fallback.data/nohttps.yml
Normal file
40
test/test_fallback.data/nohttps.yml
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
volumes:
|
||||||
|
nginx_conf:
|
||||||
|
|
||||||
|
|
||||||
|
services:
|
||||||
|
sut:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
|
volumes:
|
||||||
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
|
environment: &nginxProxyEnv
|
||||||
|
HTTPS_METHOD: nohttps
|
||||||
|
|
||||||
|
sut-dockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
environment: *nginxProxyEnv
|
||||||
|
|
||||||
|
sut-nginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
||||||
|
|
||||||
|
http-only:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "83"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: "83"
|
||||||
|
VIRTUAL_HOST: http-only.nginx-proxy.test
|
69
test/test_fallback.data/untrusteddefault.yml
Normal file
69
test/test_fallback.data/untrusteddefault.yml
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
volumes:
|
||||||
|
nginx_conf:
|
||||||
|
|
||||||
|
|
||||||
|
services:
|
||||||
|
sut:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
|
volumes:
|
||||||
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
|
- &certs ./withdefault.certs:/etc/nginx/certs:ro
|
||||||
|
environment: &nginxProxyEnv
|
||||||
|
TRUST_DEFAULT_CERT: "false"
|
||||||
|
|
||||||
|
sut-dockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
- *certs
|
||||||
|
environment: *nginxProxyEnv
|
||||||
|
|
||||||
|
sut-nginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
- *certs
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
||||||
|
|
||||||
|
https-and-http:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "81"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: "81"
|
||||||
|
VIRTUAL_HOST: https-and-http.nginx-proxy.test
|
||||||
|
|
||||||
|
https-only:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "82"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: "82"
|
||||||
|
VIRTUAL_HOST: https-only.nginx-proxy.test
|
||||||
|
HTTPS_METHOD: nohttp
|
||||||
|
|
||||||
|
http-only:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "83"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: "83"
|
||||||
|
VIRTUAL_HOST: http-only.nginx-proxy.test
|
||||||
|
HTTPS_METHOD: nohttps
|
||||||
|
|
||||||
|
missing-cert:
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- "84"
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: "84"
|
||||||
|
VIRTUAL_HOST: missing-cert.nginx-proxy.test
|
|
@ -1,8 +1,35 @@
|
||||||
services:
|
|
||||||
nginx-proxy:
|
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
nginx_conf:
|
||||||
- ${PYTEST_MODULE_PATH}/test_fallback.data/withdefault.certs:/etc/nginx/certs:ro
|
|
||||||
|
|
||||||
|
services:
|
||||||
|
sut:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
|
volumes:
|
||||||
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
|
- &certs ./withdefault.certs:/etc/nginx/certs:ro
|
||||||
|
|
||||||
|
sut-dockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
- *certs
|
||||||
|
|
||||||
|
sut-nginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
- *certs
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
||||||
|
|
||||||
https-and-http:
|
https-and-http:
|
||||||
image: web
|
image: web
|
|
@ -1,33 +1,36 @@
|
||||||
import pathlib
|
import os.path
|
||||||
import re
|
import re
|
||||||
from typing import List, Callable
|
|
||||||
|
|
||||||
import backoff
|
import backoff
|
||||||
import pytest
|
import pytest
|
||||||
import requests
|
import requests
|
||||||
from requests import Response
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def docker_compose_files(compose_file) -> List[str]:
|
def data_dir():
|
||||||
data_dir = pathlib.Path(__file__).parent.joinpath("test_fallback.data")
|
return f"{os.path.splitext(__file__)[0]}.data"
|
||||||
return [
|
|
||||||
data_dir.joinpath("compose.base.yml"),
|
|
||||||
data_dir.joinpath(compose_file).as_posix()
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def get(docker_compose, nginxproxy, want_err_re: re.Pattern[str]) -> Callable[[str], Response]:
|
def docker_compose_file(data_dir, compose_file):
|
||||||
|
return os.path.join(data_dir, compose_file)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def get(docker_compose, nginxproxy, want_err_re):
|
||||||
|
|
||||||
@backoff.on_exception(
|
@backoff.on_exception(
|
||||||
backoff.constant,
|
backoff.constant,
|
||||||
requests.exceptions.SSLError,
|
requests.exceptions.SSLError,
|
||||||
giveup=lambda e: want_err_re and bool(want_err_re.search(str(e))),
|
giveup=lambda e: want_err_re and want_err_re.search(str(e)),
|
||||||
interval=.3,
|
interval=.3,
|
||||||
max_tries=30,
|
max_tries=30,
|
||||||
jitter=None)
|
jitter=None)
|
||||||
def _get(url) -> Response:
|
def _get(url, expected_status_code=None):
|
||||||
return nginxproxy.get(url, allow_redirects=False)
|
if expected_status_code is None:
|
||||||
|
return nginxproxy.get_without_backoff(url, allow_redirects=False)
|
||||||
|
else:
|
||||||
|
return nginxproxy.get(url, allow_redirects=False, expected_status_code=expected_status_code)
|
||||||
|
|
||||||
return _get
|
return _get
|
||||||
|
|
||||||
|
@ -108,9 +111,9 @@ INTERNAL_ERR_RE = re.compile("TLSV1_UNRECOGNIZED_NAME")
|
||||||
# should prefer that server for handling requests for unknown vhosts.
|
# should prefer that server for handling requests for unknown vhosts.
|
||||||
("custom-fallback.yml", "http://unknown.nginx-proxy.test/", 418, None),
|
("custom-fallback.yml", "http://unknown.nginx-proxy.test/", 418, None),
|
||||||
])
|
])
|
||||||
def test_fallback(get, compose_file, url, want_code, want_err_re):
|
def test_fallback(get, url, want_code, want_err_re):
|
||||||
if want_err_re is None:
|
if want_err_re is None:
|
||||||
r = get(url)
|
r = get(url, want_code)
|
||||||
assert r.status_code == want_code
|
assert r.status_code == want_code
|
||||||
else:
|
else:
|
||||||
with pytest.raises(requests.exceptions.SSLError, match=want_err_re):
|
with pytest.raises(requests.exceptions.SSLError, match=want_err_re):
|
|
@ -1,9 +0,0 @@
|
||||||
services:
|
|
||||||
nginx-proxy:
|
|
||||||
image: nginxproxy/nginx-proxy:test
|
|
||||||
container_name: nginx-proxy
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
|
||||||
ports:
|
|
||||||
- "80:80"
|
|
||||||
- "443:443"
|
|
|
@ -1,14 +0,0 @@
|
||||||
services:
|
|
||||||
nginx-proxy:
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
|
||||||
- ${PYTEST_MODULE_PATH}/test_fallback.data/custom-fallback.conf:/etc/nginx/conf.d/zzz-custom-fallback.conf:ro
|
|
||||||
|
|
||||||
http-only:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "83"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "83"
|
|
||||||
VIRTUAL_HOST: http-only.nginx-proxy.test
|
|
||||||
HTTPS_METHOD: nohttps
|
|
|
@ -1,16 +0,0 @@
|
||||||
services:
|
|
||||||
nginx-proxy:
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
|
||||||
- ${PYTEST_MODULE_PATH}/test_fallback.data/withdefault.certs:/etc/nginx/certs:ro
|
|
||||||
environment:
|
|
||||||
HTTPS_METHOD: redirect
|
|
||||||
|
|
||||||
https-only:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "82"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "82"
|
|
||||||
HTTPS_METHOD: nohttp
|
|
||||||
VIRTUAL_HOST: https-only.nginx-proxy.test
|
|
|
@ -1,33 +0,0 @@
|
||||||
services:
|
|
||||||
nginx-proxy:
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
|
||||||
- ${PYTEST_MODULE_PATH}/test_fallback.data/withdefault.certs:/etc/nginx/certs:ro
|
|
||||||
environment:
|
|
||||||
HTTPS_METHOD: nohttp
|
|
||||||
|
|
||||||
https-only:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "82"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "82"
|
|
||||||
VIRTUAL_HOST: https-only.nginx-proxy.test
|
|
||||||
|
|
||||||
missing-cert:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "84"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "84"
|
|
||||||
VIRTUAL_HOST: missing-cert.nginx-proxy.test
|
|
||||||
|
|
||||||
missing-cert-default-untrusted:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "85"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "85"
|
|
||||||
VIRTUAL_HOST: missing-cert.default-untrusted.nginx-proxy.test
|
|
||||||
labels:
|
|
||||||
com.github.nginx-proxy.nginx-proxy.trust-default-cert: "false"
|
|
|
@ -1,15 +0,0 @@
|
||||||
services:
|
|
||||||
nginx-proxy:
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
|
||||||
- ${PYTEST_MODULE_PATH}/test_fallback.data/withdefault.certs:/etc/nginx/certs:ro
|
|
||||||
environment:
|
|
||||||
HTTPS_METHOD: nohttp
|
|
||||||
|
|
||||||
https-only:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "82"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "82"
|
|
||||||
VIRTUAL_HOST: https-only.nginx-proxy.test
|
|
|
@ -1,13 +0,0 @@
|
||||||
services:
|
|
||||||
nginx-proxy:
|
|
||||||
environment:
|
|
||||||
HTTPS_METHOD: redirect
|
|
||||||
|
|
||||||
http-only:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "83"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "83"
|
|
||||||
HTTPS_METHOD: nohttps
|
|
||||||
VIRTUAL_HOST: http-only.nginx-proxy.test
|
|
|
@ -1,12 +0,0 @@
|
||||||
services:
|
|
||||||
nginx-proxy:
|
|
||||||
environment:
|
|
||||||
HTTPS_METHOD: nohttps
|
|
||||||
|
|
||||||
http-only:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "83"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "83"
|
|
||||||
VIRTUAL_HOST: http-only.nginx-proxy.test
|
|
|
@ -1,41 +0,0 @@
|
||||||
services:
|
|
||||||
nginx-proxy:
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
|
||||||
- ${PYTEST_MODULE_PATH}/test_fallback.data/withdefault.certs:/etc/nginx/certs:ro
|
|
||||||
environment:
|
|
||||||
TRUST_DEFAULT_CERT: "false"
|
|
||||||
|
|
||||||
https-and-http:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "81"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "81"
|
|
||||||
VIRTUAL_HOST: https-and-http.nginx-proxy.test
|
|
||||||
|
|
||||||
https-only:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "82"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "82"
|
|
||||||
VIRTUAL_HOST: https-only.nginx-proxy.test
|
|
||||||
HTTPS_METHOD: nohttp
|
|
||||||
|
|
||||||
http-only:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "83"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "83"
|
|
||||||
VIRTUAL_HOST: http-only.nginx-proxy.test
|
|
||||||
HTTPS_METHOD: nohttps
|
|
||||||
|
|
||||||
missing-cert:
|
|
||||||
image: web
|
|
||||||
expose:
|
|
||||||
- "84"
|
|
||||||
environment:
|
|
||||||
WEB_PORTS: "84"
|
|
||||||
VIRTUAL_HOST: missing-cert.nginx-proxy.test
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
import os
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
def test_arbitrary_headers_are_passed_on(docker_compose, nginxproxy):
|
def test_arbitrary_headers_are_passed_on(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("http://web.nginx-proxy.tld/headers", headers={'Foo': 'Bar'})
|
r = nginxproxy.get("http://web.nginx-proxy.tld/headers", headers={'Foo': 'Bar'})
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
@ -91,9 +95,13 @@ def test_httpoxy_safe(docker_compose, nginxproxy):
|
||||||
assert "Proxy:" not in r.text
|
assert "Proxy:" not in r.text
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(
|
||||||
|
condition = os.environ.get("COMPOSE_PROFILES") == "separateContainers",
|
||||||
|
reason = "This test is expected to fail when using separate containers",
|
||||||
|
)
|
||||||
def test_no_host_server_tokens_off(docker_compose, nginxproxy):
|
def test_no_host_server_tokens_off(docker_compose, nginxproxy):
|
||||||
ip = nginxproxy.get_ip()
|
ip = nginxproxy.get_ip()
|
||||||
r = nginxproxy.get(f"http://{ip}/headers")
|
r = nginxproxy.get(f"http://{ip}/headers", expected_status_code=503)
|
||||||
assert r.status_code == 503
|
assert r.status_code == 503
|
||||||
assert r.headers["Server"] == "nginx"
|
assert r.headers["Server"] == "nginx"
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
|
volumes:
|
||||||
|
nginx_conf:
|
||||||
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
web:
|
web:
|
||||||
image: web
|
image: web
|
||||||
expose:
|
expose:
|
||||||
- "80"
|
- "80"
|
||||||
environment:
|
environment:
|
||||||
WEB_PORTS: "80"
|
WEB_PORTS: 80
|
||||||
VIRTUAL_HOST: web.nginx-proxy.tld
|
VIRTUAL_HOST: web.nginx-proxy.tld
|
||||||
|
|
||||||
web-server-tokens-off:
|
web-server-tokens-off:
|
||||||
|
@ -12,6 +16,31 @@ services:
|
||||||
expose:
|
expose:
|
||||||
- "80"
|
- "80"
|
||||||
environment:
|
environment:
|
||||||
WEB_PORTS: "80"
|
WEB_PORTS: 80
|
||||||
VIRTUAL_HOST: web-server-tokens-off.nginx-proxy.tld
|
VIRTUAL_HOST: web-server-tokens-off.nginx-proxy.tld
|
||||||
SERVER_TOKENS: "off"
|
SERVER_TOKENS: "off"
|
||||||
|
|
||||||
|
sut:
|
||||||
|
profiles:
|
||||||
|
- singleContainer
|
||||||
|
image: nginxproxy/nginx-proxy:test
|
||||||
|
volumes:
|
||||||
|
- &dockerSocket /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
|
|
||||||
|
sut-dockergen:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
image: nginxproxy/nginx-proxy:test-dockergen
|
||||||
|
volumes:
|
||||||
|
- &confVolume nginx_conf:/etc/nginx/conf.d
|
||||||
|
- *dockerSocket
|
||||||
|
|
||||||
|
sut-nginx:
|
||||||
|
profiles:
|
||||||
|
- separateContainers
|
||||||
|
container_name: nginx-proxy
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- *confVolume
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx-proxy.nginx"
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue