1
0
mirror of https://github.com/thib8956/nginx-proxy synced 2025-07-01 22:35:45 +00:00

124 Commits
0.1.0 ... 0.4.0

Author SHA1 Message Date
0ef8dca98c Merge pull request #443 from jwilder/jw-circle
Remove circle.yml
2016-05-04 16:52:41 -06:00
c30640b024 Remove circle.yml
Using travis now
2016-05-04 16:05:03 -06:00
e964b9d29f Merge pull request #441 from jwilder/jw-0.7.1
Update to docker-gen 0.7.1
2016-05-04 16:03:52 -06:00
d40d1d4052 Update to docker-gen 0.7.1 2016-05-04 14:51:30 -06:00
5b9264d945 Merge pull request #298 from kamermans/master
Added env var to disable SSL redirect
2016-05-01 17:45:45 -06:00
1c98df2de4 Merge pull request #337 from baptistedonaux/master
Support Overlay Network
2016-05-01 17:35:48 -06:00
a72c7e6e20 Add docker hub badges 2016-04-22 09:56:21 -06:00
786a2375aa Using different path method to hopefully fix cert volume issue in CI systems 2016-04-20 12:47:14 -04:00
f296b273ce Normalized indentation, added explicit CERT_NAME 2016-04-20 12:33:21 -04:00
1d0a5d9194 Added SSL/HTTPS tests 2016-04-20 12:20:08 -04:00
f294582ae8 Merge branch 'master' of https://github.com/jwilder/nginx-proxy 2016-04-20 11:05:20 -04:00
f66934567e Merge remote-tracking branch 'upstream/master' 2016-04-05 09:22:31 +02:00
48c31836be Merge pull request #407 from jwilder/jw-travis
Add travis.yml
2016-04-04 17:27:12 -06:00
30e2da01e9 Add travis.yml
From #312
2016-04-04 17:18:05 -06:00
59f8d4301a Merge pull request #406 from jwilder/jw-forego
Switch forego downloads
2016-04-04 16:56:49 -06:00
a54af7d73b Switch forego downloads
Official ddollar site has been down for a while and not sure if it's
coming back up or not.
2016-04-04 16:48:52 -06:00
6928d62e1e Upgrade Docker dependency from 1.9 to 1.10 2016-03-24 14:40:50 +01:00
88cc096aaf [FIX] expose port for separated containers test 2016-03-24 11:16:22 +01:00
6b88365465 Merge remote-tracking branch 'upstream/master' 2016-03-24 08:41:23 +01:00
6adc04be83 Merge pull request #396 from pitkley/docker-gen-0.7.0
Update to docker-gen 0.7.0
2016-03-23 11:33:00 -06:00
86aea653c8 Update to docker-gen 0.7.0
Since [1] some timings seem to have changed. This caused the unit tests
to fail intermittently, from my testings especially on Ubuntu systems
(much less often on e.g. Arch).

This commit adds the `dockergen_wait_for_event` helper-function to try
and wait for the configuration to be generated by docker-gen before
continuing on with the actual tests themselves.

Additionally, at the end of every test file, all containers spun up by
the bats-tests will be stopped. This required adding the `bats-type`
label to every container started during the bats-tests.

The stopping of the containers reduces the amount of events docker-gen
has to process, thus resulting in lower wait times for the generation to
happen.

[1]: 50435652b1
2016-03-23 17:45:34 +01:00
74fb2acc4b Merge remote-tracking branch 'upstream/master' 2016-03-23 08:52:58 +01:00
f2966c5db1 Merge pull request #374 from mrohland/master
updated to nginx 1.9.12
2016-03-21 16:28:18 -06:00
1f8b480365 moved nginx to 1.9.12 2016-03-21 22:47:01 +01:00
9671e0c1a2 Merge branch 'master' of https://github.com/jwilder/nginx-proxy 2016-03-18 13:47:11 -04:00
87d0c68aac Merge pull request #371 from gabriel403/patch-1
Mention the docker-compose networking issues
2016-03-03 15:35:43 -07:00
b42cd85570 Unit test - removed network creation 2016-02-26 09:12:11 +01:00
47631eb5de Merge remote-tracking branch 'upstream/master' 2016-02-25 09:59:04 +01:00
2b418319f2 Test to support overlay networks 2016-02-25 09:58:17 +01:00
163d1ce174 Upgrade docker-gen from 0.5.0 to 0.7.0 2016-02-25 08:26:41 +01:00
ebab7cf2b9 [TEMPLATE] fix variable call 2016-02-23 13:59:30 +01:00
d849e5f5d0 Linkify the issue ref 2016-02-23 07:25:32 +00:00
8c6ec27547 Mention the docker-compose issues 2016-02-23 07:23:27 +00:00
2b24c1712a Merge pull request #342 from schmunk42/feature/updated-nginx
updated nginx to 1.9.9
2016-02-14 16:20:48 -07:00
658e20f661 Support container in one network shared with current container 2016-02-05 09:16:43 +01:00
9341f54e51 Merge pull request #1 from appropriate/overlay-support
Update README wording for overlay networking
2016-01-22 08:44:43 +01:00
5c828cb50a Update README wording for overlay networking 2016-01-20 23:30:55 -08:00
627f789f19 updated nginx to 1.9.9 2016-01-20 23:26:21 +01:00
2eb977589c Complete documentation 2016-01-20 08:54:57 +01:00
99963b4ab0 Merge branch 'master' of https://github.com/jwilder/nginx-proxy 2016-01-18 16:37:03 -05:00
13bb99c4d2 Update README - Nginx proxy must use host network to support overlay network 2016-01-17 12:35:29 +01:00
a66115f560 Use new Network interface to support new overlay network 2016-01-17 12:29:55 +01:00
deb4050fa4 UPGRADE docker-gen release from 0.4.2 to 0.5.0 2016-01-17 12:28:59 +01:00
f42bf9175d Merge pull request #314 from thomasleveil/circleci-docker1.9.1
CircleCI: make tests run on Docker 1.9.1
2015-12-15 07:38:25 -07:00
c11af183d8 CircleCI: make tests run on Docker 1.9.1
See CircleCI announcement https://discuss.circleci.com/t/docker-1-9-1-is-available/1009
2015-12-14 09:25:21 +01:00
4a95d84d0d Trying to trigger CI 2015-12-02 21:46:05 -05:00
58fc298dea Semantic change to trigger CI rebuild 2015-12-02 00:29:32 -05:00
4bf7f888d0 Documented HTTPS_METHOD 2015-11-20 17:47:08 -05:00
97c6340a9f Implemented HTTPS noredir 2015-11-20 17:37:06 -05:00
9dd6ad8503 First try at HTTPS_METHOD 2015-11-20 16:53:50 -05:00
205b391677 Merge pull request #277 from TravisDart/master
Typo fix.
2015-10-30 15:11:37 -06:00
8225015c85 Typo fix. 2015-10-30 15:48:01 -05:00
588cdf6266 Update email address 2015-10-29 13:32:09 -06:00
1e7136470e Merge pull request #276 from appropriate/nginx-1.9.6
Update to nginx 1.9.6
2015-10-29 13:31:34 -06:00
5404938797 Update to nginx 1.9.6 2015-10-28 21:29:39 -07:00
defed25585 Merge pull request #265 from thomasleveil/makefile
makefile: add target to update docker images tests depend on
2015-10-17 16:12:05 -06:00
66d401c075 makefile: update docker images before running tests 2015-10-14 19:42:59 +00:00
1ea74e9b0a Merge pull request #264 from jwilder/jw-tests
Testing fixups
2015-10-14 11:02:33 -06:00
db7a5ab2ae Use make test from circle.yml 2015-10-14 10:48:43 -06:00
e09550febb Add Makefile
Make it easier to run tests.
2015-10-14 10:29:22 -06:00
1786edd87d Update .dockerignore to recent file additions 2015-10-14 10:19:50 -06:00
1670bccd17 Merge pull request #262 from mariusGundersen/master
Use trim to handle VIRTUAL_HOST values with spaces
2015-10-14 09:56:33 -06:00
b075fcf250 Merge pull request #260 from appropriate/test-trap-term
Trap TERM to allow quicker exit of web containers
2015-10-14 09:55:49 -06:00
3e530a0784 Use latest docker-gen 2015-10-13 21:49:19 +02:00
1e0b930174 trim whitespace from host and port
based on latest docker-gen
2015-10-13 21:48:59 +02:00
098b058b0d Trap TERM to allow quicker exit of web containers 2015-10-12 21:53:52 -07:00
f819a4e2e7 Merge pull request #257 from appropriate/osx-fix-test-suite
Fix running test suite under Docker Machine
2015-10-12 22:43:19 -06:00
1d9db94535 Use docker:1.7 image to match CircleCI's version 2015-10-12 21:16:55 -07:00
5c6a63568d Remove --rm from docker run commands
Trying to avoid "Failed to destroy btrfs snapshot" errors on CircleCI
2015-10-12 20:59:53 -07:00
6e7ff3451b Remove commented out line 2015-10-11 11:27:38 -07:00
ec393e3d18 Pull additional images needed for Circle CI 2015-10-11 11:05:08 -07:00
30e565f0ed Check "docker version" from a sibling container 2015-10-11 11:04:00 -07:00
b1a08843da Avoid breakage from IFS being reset in "run"
See https://github.com/sstephenson/bats/issues/89
2015-10-11 10:55:17 -07:00
0d2bc38179 Run curl commands from a sibling container 2015-10-09 23:11:03 -07:00
2cba8a8d5f Avoid xargs -r
The -r option is not available on OS X
2015-10-09 23:10:38 -07:00
5fe9411d88 Change line endings to Unix 2015-10-09 23:07:46 -07:00
df6778f673 Merge pull request #256 from thomasleveil/patch-1
fix CircleCI badge link
2015-10-09 09:53:38 -06:00
99560da65c fix CircleCI badge link 2015-10-09 15:33:29 +02:00
495b0ad8b6 Add circleci build status badge 2015-10-08 22:08:04 -06:00
289a519dce Merge pull request #246 from thomasleveil/fix/197
add test suite. See #197
2015-10-08 22:01:15 -06:00
a81bb961e4 Merge pull request #252 from lsde/master
fix condition for default config location
2015-10-08 20:53:16 -06:00
5c2280df84 fix condition for default config location
Signed-off-by: Jonas Svatos <jonas.svatos@etnetera.cz>
2015-10-08 12:03:28 +02:00
2ffa3a182f Merge pull request #248 from appropriate/missing-access-log
Add missing access_log statement to HTTPS fallback
2015-10-07 09:04:13 -06:00
6b5e12a946 Add missing access_log statement to HTTPS fallback 2015-10-06 21:18:00 -07:00
92be4b6d54 doc 2015-09-30 23:57:13 +00:00
865b14e029 tests: fix minor bug in docker_wait_for_log 2015-09-30 23:56:25 +00:00
b4d1acbf2d tests: refactoring 2015-09-30 23:40:04 +00:00
3f223abeb8 Merge pull request #240 from rarous/master
Update to nginx 1.9.5 with HTTP/2 support
2015-09-30 12:01:09 -06:00
4bd30f5d2c add test suite. See #197
This test suite is implemented using [bats](https://github.com/sstephenson/bats).

Not all features are tested. For instance ssl features and custom nginx config are missing. Probably others.

This test suite won't work with TravisCI. Too many evenings were wasted trying to overcome [issues](http://stackoverflow.com/questions/32846800/travis-fails-to-stop-docker-containers) that arises only on the TravisCI platform. However it runs on [CircleCI](https://circleci.com) which is also free for opensource projects.
2015-09-29 23:46:36 +00:00
d46d9ad943 Use nginx 1.9.5 badge in readme 2015-09-23 17:49:51 +02:00
e06d5917a2 Use HTTP/2 instead of SPDY 2015-09-23 17:48:40 +02:00
249fb204f1 Use HTTP/2 instead of SPDY 2015-09-23 17:47:18 +02:00
d45c10ca66 Use nginx 1.9.5 2015-09-23 17:43:31 +02:00
8c193ba7e1 Merge pull request #215 from gradecam/feature/customize_improvements
customizability improvements
2015-09-12 15:23:53 -06:00
bddb647b5f Merge pull request #230 from appropriate/remove_duplicate_access_log_entries
Remove duplicate access log entries
2015-09-12 15:12:31 -06:00
9e55f5382f Merge pull request #225 from funkygibbing/default-ssl
Fix for #188 - remove hostname from default SSL block
2015-09-12 15:05:53 -06:00
83b0b25c26 Merge pull request #228 from berfarah/patch-1
Bumping to latest Docker Gen version
2015-09-12 15:00:00 -06:00
9440ebed09 Merge pull request #235 from thomasleveil/safeguard
refuse to start if the docker sock isn't available
2015-09-12 14:56:39 -06:00
d21c6943a2 Merge pull request #1 from appropriate/safeguard
Adjust entrypoint to always warn on missing socket
2015-09-12 16:52:01 +02:00
f95ff82ad0 Adjust entrypoint to always warn on missing socket
Also chmod the entrypoint
2015-09-12 07:23:36 -07:00
1aac996019 conform to Docker official images best practices
https://github.com/docker-library/official-images/blob/master/README.md#consistency
2015-09-12 10:37:21 +00:00
d83ba3da1e refuse to start if the docker sock isn't available 2015-09-11 23:42:44 +00:00
900a676af8 Move access_log from the http level to server
This prevents duplicate access_log entries from being written for each request
2015-09-03 08:33:33 -07:00
d844c124ff Bumping to latest Docker Gen version
As per [this](https://github.com/jwilder/docker-gen/issues/114) ticket. Thanks again!
2015-09-01 09:38:43 -07:00
ae0da36d75 Fix bugs in config file from refactor 2015-08-29 18:38:43 -06:00
e47df7a884 Merge pull request #224 from appropriate/remove-boot2docker-instructions
Remove boot2docker-specific instructions
2015-08-26 15:46:04 -06:00
d066bd32e0 Fix for #188 - add SSL server block outside hosts loop 2015-08-26 18:35:47 +10:00
d3f56468b1 Fix for #188 - remove hostname from default SSL block 2015-08-26 12:49:59 +10:00
e5ad1ecfd1 Remove boot2docker-specific instructions
Both boot2docker and docker-machine will work fine with the normal `docker run`
command used in the rest of the docs.
2015-08-24 19:37:45 -07:00
214ff827d3 Merge pull request #223 from appropriate/readme-expose-requirement
Document the need to EXPOSE the proxied port
2015-08-23 20:59:51 -06:00
682914dec7 Document the need to EXPOSE the proxied port 2015-08-23 18:41:30 -07:00
924fcd7984 Remove error_log setting from nginx.tmpl
It's already set correctly in nginx.conf
2015-08-23 09:00:23 -07:00
24859e3f92 Merge pull request #220 from RichLewis007/RichLewis007-patch-typos-1
fix 2 minor English language typos
2015-08-19 22:34:22 -06:00
235528338c fix 2 minor English language typos 2015-08-19 23:52:54 -04:00
5089bf77dd Update wording in docs as per pull request feedback 2015-08-14 12:34:10 -06:00
66711ff026 Add warning about consequences of incorrectly using proxy_config.conf 2015-08-14 12:26:19 -06:00
405f4876b9 As per pull request feedback, update names to be consistent 2015-08-14 12:26:19 -06:00
d9ee7ed704 Add support for adding options to the location block of a vhost 2015-08-14 12:26:19 -06:00
b131b00e19 Add support for vhosts.d/defaults file with default vhost options
- Only used if it exists and a vhost-specific one doesn't
2015-08-14 12:26:19 -06:00
d07a7d7487 Fix example command in docs 2015-08-14 12:26:19 -06:00
2eff96969a Add support for overriding default proxy settings
- If /etc/nginx/proxy.conf exists use that, otherwise use the default
2015-08-14 12:26:07 -06:00
23ae831198 Merge pull request #202 from webner/default_host
set default_server also for https
2015-08-11 23:41:53 -06:00
6965b1ead4 fallback when DEFAULT_HOST is not set 2015-07-26 11:38:45 +02:00
b0647dd5e9 set default_server also for https 2015-07-24 10:39:56 +02:00
22 changed files with 1897 additions and 46 deletions

View File

@ -1,2 +1,6 @@
.git
.dockerignore
circle.yml
Makefile
README.md
test

22
.travis.yml Normal file
View File

@ -0,0 +1,22 @@
sudo: required
services:
- docker
env:
global:
- DOCKER_VERSION=1.10.1-0~trusty
before_install:
# list docker-engine versions
- apt-cache madison docker-engine
# upgrade docker-engine to specific version
- sudo apt-get -o Dpkg::Options::="--force-confnew" install -y docker-engine=${DOCKER_VERSION}
- docker version
- docker info
- sudo add-apt-repository ppa:duggan/bats --yes
- sudo apt-get update -qq
- sudo apt-get install -qq bats
- make update-dependencies
script:
- make test

View File

@ -1,5 +1,5 @@
FROM nginx:1.9.2
MAINTAINER Jason Wilder jwilder@litl.com
FROM nginx:1.9.12
MAINTAINER Jason Wilder mail@jasonwilder.com
# Install wget and install/updates certificates
RUN apt-get update \
@ -14,10 +14,10 @@ RUN echo "daemon off;" >> /etc/nginx/nginx.conf \
&& sed -i 's/^http {/&\n server_names_hash_bucket_size 128;/g' /etc/nginx/nginx.conf
# Install Forego
RUN wget -P /usr/local/bin https://godist.herokuapp.com/projects/ddollar/forego/releases/current/linux-amd64/forego \
&& chmod u+x /usr/local/bin/forego
ADD https://github.com/jwilder/forego/releases/download/v0.16.1/forego /usr/local/bin/forego
RUN chmod u+x /usr/local/bin/forego
ENV DOCKER_GEN_VERSION 0.4.0
ENV DOCKER_GEN_VERSION 0.7.1
RUN wget https://github.com/jwilder/docker-gen/releases/download/$DOCKER_GEN_VERSION/docker-gen-linux-amd64-$DOCKER_GEN_VERSION.tar.gz \
&& tar -C /usr/local/bin -xvzf docker-gen-linux-amd64-$DOCKER_GEN_VERSION.tar.gz \
@ -30,4 +30,5 @@ ENV DOCKER_HOST unix:///tmp/docker.sock
VOLUME ["/etc/nginx/certs"]
ENTRYPOINT ["/app/docker-entrypoint.sh"]
CMD ["forego", "start", "-r"]

14
Makefile Normal file
View File

@ -0,0 +1,14 @@
.SILENT :
.PHONY : test
update-dependencies:
docker pull jwilder/docker-gen:0.7.1
docker pull nginx:1.9.12
docker pull python:3
docker pull rancher/socat-docker:latest
docker pull appropriate/curl:latest
docker pull docker:1.10
test:
docker build -t jwilder/nginx-proxy:bats .
bats test

View File

@ -1,4 +1,5 @@
![nginx 1.9.2](https://img.shields.io/badge/nginx-1.9.2-brightgreen.svg) ![License MIT](https://img.shields.io/badge/license-MIT-blue.svg)
![nginx 1.9.12](https://img.shields.io/badge/nginx-1.9.12-brightgreen.svg) ![License MIT](https://img.shields.io/badge/license-MIT-blue.svg) [![Build](https://circleci.com/gh/jwilder/nginx-proxy.svg?&style=shield&circle-token=2da3ee844076a47371bd45da81cf27409ca7306a)](https://circleci.com/gh/jwilder/nginx-proxy) [![Build Status](https://travis-ci.org/jwilder/nginx-proxy.svg?branch=master)](https://travis-ci.org/jwilder/nginx-proxy) [![](https://img.shields.io/docker/stars/jwilder/nginx-proxy.svg)](https://hub.docker.com/r/jwilder/nginx-proxy 'DockerHub') [![](https://img.shields.io/docker/pulls/jwilder/nginx-proxy.svg)](https://hub.docker.com/r/jwilder/nginx-proxy 'DockerHub')
nginx-proxy sets up a container running nginx and [docker-gen][1]. docker-gen generates reverse proxy configs for nginx and reloads nginx when containers are started and stopped.
@ -14,12 +15,14 @@ Then start any containers you want proxied with an env var `VIRTUAL_HOST=subdoma
$ docker run -e VIRTUAL_HOST=foo.bar.com ...
The containers being proxied must [expose](https://docs.docker.com/reference/run/#expose-incoming-ports) the port to be proxied, either by using the `EXPOSE` directive in their `Dockerfile` or by using the `--expose` flag to `docker run` or `docker create`.
Provided your DNS is setup to forward foo.bar.com to the a host running nginx-proxy, the request will be routed to a container with the VIRTUAL_HOST env var set.
If your are using `boot2docker` start `nginx-proxy` with:
### Docker-compose
Currently this does not work with the new v2 syntax of docker-compose (due to not being compatible with the new network overlay see [#304](https://github.com/jwilder/nginx-proxy/issues/304)). It does work when using the old docker-composer syntax.
$ $(boot2docker shellinit)
$ docker run -p 80:80 -e DOCKER_HOST -e DOCKER_CERT_PATH -e DOCKER_TLS_VERIFY -v $DOCKER_CERT_PATH:$DOCKER_CERT_PATH -it jwilder/nginx-proxy
### Multiple Ports
@ -36,6 +39,20 @@ If you need to support multiple virtual hosts for a container, you can separate
You can also use wildcards at the beginning and the end of host name, like `*.bar.com` or `foo.bar.*`. Or even a regular expression, which can be very useful in conjunction with a wildcard DNS service like [xip.io](http://xip.io), using `~^foo\.bar\..*\.xip\.io` will match `foo.bar.127.0.0.1.xip.io`, `foo.bar.10.0.2.2.xip.io` and all other given IPs. More information about this topic can be found in the nginx documentation about [`server_names`](http://nginx.org/en/docs/http/server_names.html).
### Multiple Networks
With the addition of [overlay networking](https://docs.docker.com/engine/userguide/networking/get-started-overlay/) in Docker 1.9, your `nginx-proxy` container may need to connect to backend containers on multiple networks. By default, if you don't pass the `--net` flag when your `nginx-proxy` container is created, it will only be attached to the default `bridge` network. This means that it will not be able to connect to containers on networks other than `bridge`.
If you want your `nginx-proxy` container to be attached to a different network, you must pass the `--net=my-network` option in your `docker create` or `docker run` command. At the time of this writing, only a single network can be specified at container creation time. To attach to other networks, you can use the `docker network connect` command after your container is created:
```console
$ docker run -d -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock:ro \
--name my-nginx-proxy --net my-network jwilder/nginx-proxy
$ docker network connect my-other-network my-nginx-proxy
```
In this example, the `my-nginx-proxy` container will be connected to `my-network` and `my-other-network` and will be able to proxy to other containers attached to those networks.
### SSL Backends
If you would like to connect to your backend using HTTPS instead of HTTP, set `VIRTUAL_PROTO=https` on the backend container.
@ -96,7 +113,7 @@ should have a `foo.bar.com.dhparam.pem` file in the certs directory.
#### Wildcard Certificates
Wildcard certificates and keys should be name after the domain name with a `.crt` and `.key` extension.
Wildcard certificates and keys should be named after the domain name with a `.crt` and `.key` extension.
For example `VIRTUAL_HOST=foo.bar.com` would use cert name `bar.com.crt` and `bar.com.key`.
#### SNI
@ -113,7 +130,7 @@ should provide compatibility with clients back to Firefox 1, Chrome 1, IE 7, Ope
Windows XP IE8, Android 2.3, Java 7. The configuration also enables HSTS, and SSL
session caches.
The behavior for the proxy when port 80 and 443 are exposed is as follows:
The default behavior for the proxy when port 80 and 443 are exposed is as follows:
* If a container has a usable cert, port 80 will redirect to 443 for that container so that HTTPS
is always preferred when available.
@ -124,6 +141,10 @@ to establish a connection. A self-signed or generic cert named `default.crt` an
will allow a client browser to make a SSL connection (likely w/ a warning) and subsequently receive
a 503.
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`.
### Basic Authentication Support
In order to be able to secure your virtual host, you have to create a file named as its equivalent VIRTUAL_HOST variable on directory
@ -143,6 +164,25 @@ You'll need apache2-utils on the machine where you plan to create the htpasswd f
If you need to configure Nginx beyond what is possible using environment variables, you can provide custom configuration files on either a proxy-wide or per-`VIRTUAL_HOST` basis.
#### Replacing default proxy settings
If you want to replace the default proxy settings for the nginx container, add a configuration file at `/etc/nginx/proxy.conf`. A file with the default settings would
look like this:
```Nginx
# HTTP 1.1 support
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $proxy_connection;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
```
***NOTE***: If you provide this file it will replace the defaults; you may want to check the .tmpl file to make sure you have all of the needed options.
#### Proxy-wide
To add settings on a proxy-wide basis, add your configuration file under `/etc/nginx/conf.d` using a name ending in `.conf`.
@ -163,7 +203,7 @@ Or it can be done by mounting in your custom configuration in your `docker run`
#### Per-VIRTUAL_HOST
To add settings on a per-`VIRTUAL_HOST` basis, add your configuration file under `/etc/nginx/vhost.d`. Unlike in the proxy-wide case, which allows mutliple config files with any name ending in `.conf`, the per-`VIRTUAL_HOST` file must be named exactly after the `VIRTUAL_HOST`.
To add settings on a per-`VIRTUAL_HOST` basis, add your configuration file under `/etc/nginx/vhost.d`. Unlike in the proxy-wide case, which allows multiple config files with any name ending in `.conf`, the per-`VIRTUAL_HOST` file must be named exactly after the `VIRTUAL_HOST`.
In order to allow virtual hosts to be dynamically configured as backends are added and removed, it makes the most sense to mount an external directory as `/etc/nginx/vhost.d` as opposed to using derived images or mounting individual configuration files.
@ -175,4 +215,39 @@ For example, if you have a virtual host named `app.example.com`, you could provi
If you are using multiple hostnames for a single container (e.g. `VIRTUAL_HOST=example.com,www.example.com`), the virtual host configuration file must exist for each hostname. If you would like to use the same configuration for multiple virtual host names, you can use a symlink:
$ { echo 'server_tokens off;'; echo 'client_max_body_size 100m;'; } > /path/to/vhost.d/www.example.com
$ ln -s www.example.com /path/to/vhost.d/example.com
$ ln -s /path/to/vhost.d/www.example.com /path/to/vhost.d/example.com
#### Per-VIRTUAL_HOST default configuration
If you want most of your virtual hosts to use a default single configuration and then override on a few specific ones, add those settings to the `/etc/nginx/vhost.d/default` file. This file
will be used on any virtual host which does not have a `/etc/nginx/vhost.d/{VIRTUAL_HOST}` file associated with it.
#### Per-VIRTUAL_HOST location configuration
To add settings to the "location" block on a per-`VIRTUAL_HOST` basis, add your configuration file under `/etc/nginx/vhost.d`
just like the previous section except with the suffix `_location`.
For example, if you have a virtual host named `app.example.com` and you have configured a proxy_cache `my-cache` in another custom file, you could tell it to use a proxy cache as follows:
$ docker run -d -p 80:80 -p 443:443 -v /path/to/vhost.d:/etc/nginx/vhost.d:ro -v /var/run/docker.sock:/tmp/docker.sock:ro jwilder/nginx-proxy
$ { echo 'proxy_cache my-cache;'; echo 'proxy_cache_valid 200 302 60m;'; echo 'proxy_cache_valid 404 1m;' } > /path/to/vhost.d/app.example.com_location
If you are using multiple hostnames for a single container (e.g. `VIRTUAL_HOST=example.com,www.example.com`), the virtual host configuration file must exist for each hostname. If you would like to use the same configuration for multiple virtual host names, you can use a symlink:
$ { echo 'proxy_cache my-cache;'; echo 'proxy_cache_valid 200 302 60m;'; echo 'proxy_cache_valid 404 1m;' } > /path/to/vhost.d/app.example.com_location
$ ln -s /path/to/vhost.d/www.example.com /path/to/vhost.d/example.com
#### Per-VIRTUAL_HOST location default configuration
If you want most of your virtual hosts to use a default single `location` block configuration and then override on a few specific ones, add those settings to the `/etc/nginx/vhost.d/default_location` file. This file
will be used on any virtual host which does not have a `/etc/nginx/vhost.d/{VIRTUAL_HOST}` file associated with it.
### Contributing
Before submitting pull requests or issues, please check github to make sure an existing issue or pull request is not already open.
#### Running Tests Locally
To run tests, you'll need to install [bats 0.4.0](https://github.com/sstephenson/bats).
make test

22
docker-entrypoint.sh Executable file
View File

@ -0,0 +1,22 @@
#!/bin/bash
set -e
# Warn if the DOCKER_HOST socket does not exist
if [[ $DOCKER_HOST == unix://* ]]; then
socket_file=${DOCKER_HOST#unix://}
if ! [ -S $socket_file ]; then
cat >&2 <<-EOT
ERROR: you need to share your Docker host socket with a volume at $socket_file
Typically you should run your jwilder/nginx-proxy with: \`-v /var/run/docker.sock:$socket_file:ro\`
See the documentation at http://git.io/vZaGJ
EOT
socketMissing=1
fi
fi
# If the user has run the default command and the socket doesn't exist, fail
if [ "$socketMissing" = 1 -a "$1" = forego -a "$2" = start -a "$3" = '-r' ]; then
exit 1
fi
exec "$@"

View File

@ -1,3 +1,5 @@
{{ $CurrentContainer := where $ "ID" .Docker.CurrentContainerID | first }}
{{ define "upstream" }}
{{ if .Address }}
{{/* If we got the containers from swarm and this container's port is published to host, use host IP:PORT */}}
@ -5,13 +7,13 @@
# {{ .Container.Node.Name }}/{{ .Container.Name }}
server {{ .Container.Node.Address.IP }}:{{ .Address.HostPort }};
{{/* If there is no swarm node or the port is not published on host, use container's IP:PORT */}}
{{ else }}
{{ else if .Network }}
# {{ .Container.Name }}
server {{ .Address.IP }}:{{ .Address.Port }};
server {{ .Network.IP }}:{{ .Address.Port }};
{{ end }}
{{ else }}
{{ else if .Network }}
# {{ .Container.Name }}
server {{ .Container.IP }} down;
server {{ .Network.IP }} down;
{{ end }}
{{ end }}
@ -35,9 +37,11 @@ log_format vhost '$host $remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
access_log /proc/self/fd/1 vhost;
error_log /proc/self/fd/2;
access_log off;
{{ if (exists "/etc/nginx/proxy.conf") }}
include /etc/nginx/proxy.conf;
{{ else }}
# HTTP 1.1 support
proxy_http_version 1.1;
proxy_buffering off;
@ -47,34 +51,63 @@ proxy_set_header Connection $proxy_connection;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
{{ end }}
server {
listen 80;
server_name _; # This is just an invalid value which will never trigger on a real hostname.
listen 80;
access_log /var/log/nginx/access.log vhost;
return 503;
}
{{ if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }}
server {
server_name _; # This is just an invalid value which will never trigger on a real hostname.
listen 443 ssl http2;
access_log /var/log/nginx/access.log vhost;
return 503;
ssl_certificate /etc/nginx/certs/default.crt;
ssl_certificate_key /etc/nginx/certs/default.key;
}
{{ end }}
{{ range $host, $containers := groupByMulti $ "Env.VIRTUAL_HOST" "," }}
upstream {{ $host }} {
{{ range $container := $containers }}
{{ $addrLen := len $container.Addresses }}
{{/* If only 1 port exposed, use that */}}
{{ if eq $addrLen 1 }}
{{ $address := index $container.Addresses 0 }}
{{ template "upstream" (dict "Container" $container "Address" $address) }}
{{/* If more than one port exposed, use the one matching VIRTUAL_PORT env var, falling back to standard web port 80 */}}
{{ else }}
{{ $port := coalesce $container.Env.VIRTUAL_PORT "80" }}
{{ $address := where $container.Addresses "Port" $port | first }}
{{ template "upstream" (dict "Container" $container "Address" $address) }}
{{ range $knownNetwork := $CurrentContainer.Networks }}
{{ range $containerNetwork := $container.Networks }}
{{ if eq $knownNetwork.Name $containerNetwork.Name }}
## Can be connect with "{{ $containerNetwork.Name }}" network
{{/* If only 1 port exposed, use that */}}
{{ if eq $addrLen 1 }}
{{ $address := index $container.Addresses 0 }}
{{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }}
{{/* If more than one port exposed, use the one matching VIRTUAL_PORT env var, falling back to standard web port 80 */}}
{{ else }}
{{ $port := coalesce $container.Env.VIRTUAL_PORT "80" }}
{{ $address := where $container.Addresses "Port" $port | first }}
{{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
}
{{ $default_host := or ($.Env.DEFAULT_HOST) "" }}
{{ $default_server := index (dict $host "" $default_host "default_server") $host }}
{{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost, falling back to "http" */}}
{{ $proto := or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http" }}
{{/* Get the HTTPS_METHOD defined by containers w/ the same vhost, falling back to "redirect" */}}
{{ $https_method := or (first (groupByKeys $containers "Env.HTTPS_METHOD")) "redirect" }}
{{/* Get the first cert name defined by containers w/ the same vhost */}}
{{ $certName := (first (groupByKeys $containers "Env.CERT_NAME")) }}
@ -88,16 +121,23 @@ upstream {{ $host }} {
{{/* Use the cert specifid on the container or fallback to the best vhost match */}}
{{ $cert := (coalesce $certName $vhostCert) }}
{{ if (and (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert))) }}
{{ $is_https := (and (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert))) }}
{{ if $is_https }}
{{ if eq $https_method "redirect" }}
server {
server_name {{ $host }};
listen 80 {{ $default_server }};
access_log /var/log/nginx/access.log vhost;
return 301 https://$host$request_uri;
}
{{ end }}
server {
server_name {{ $host }};
listen 443 ssl spdy;
listen 443 ssl http2 {{ $default_server }};
access_log /var/log/nginx/access.log vhost;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;
@ -117,47 +157,58 @@ server {
{{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }}
include {{ printf "/etc/nginx/vhost.d/%s" $host }};
{{ else if (exists "/etc/nginx/vhost.d/default") }}
include /etc/nginx/vhost.d/default;
{{ end }}
location / {
proxy_pass {{ $proto }}://{{ $host }};
proxy_pass {{ trim $proto }}://{{ trim $host }};
{{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }}
auth_basic "Restricted {{ $host }}";
auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }};
{{ end }}
{{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }}
include {{ printf "/etc/nginx/vhost.d/%s_location" $host}};
{{ else if (exists "/etc/nginx/vhost.d/default_location") }}
include /etc/nginx/vhost.d/default_location;
{{ end }}
}
}
{{ else }}
{{ end }}
{{ if or (not $is_https) (eq $https_method "noredirect") }}
server {
{{ if $.Env.DEFAULT_HOST }}
{{ if eq $.Env.DEFAULT_HOST $host }}
listen 80 default_server;
server_name {{ $host }};
{{ else }}
server_name {{ $host }};
{{ end }}
{{ else }}
server_name {{ $host }};
{{ end }}
server_name {{ $host }};
listen 80 {{ $default_server }};
access_log /var/log/nginx/access.log vhost;
{{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }}
include {{ printf "/etc/nginx/vhost.d/%s" $host }};
{{ else if (exists "/etc/nginx/vhost.d/default") }}
include /etc/nginx/vhost.d/default;
{{ end }}
location / {
proxy_pass {{ $proto }}://{{ $host }};
proxy_pass {{ trim $proto }}://{{ trim $host }};
{{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }}
auth_basic "Restricted {{ $host }}";
auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }};
{{ end }}
{{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }}
include {{ printf "/etc/nginx/vhost.d/%s_location" $host}};
{{ else if (exists "/etc/nginx/vhost.d/default_location") }}
include /etc/nginx/vhost.d/default_location;
{{ end }}
}
}
{{ if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }}
{{ if (and (not $is_https) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }}
server {
server_name {{ $host }};
listen 443 ssl spdy;
listen 443 ssl http2 {{ $default_server }};
access_log /var/log/nginx/access.log vhost;
return 503;
ssl_certificate /etc/nginx/certs/default.crt;

14
test/README.md Normal file
View File

@ -0,0 +1,14 @@
Test suite
==========
This test suite is implemented on top of the [Bats](https://github.com/sstephenson/bats/blob/master/README.md) test framework.
It is intended to verify the correct behavior of the Docker image `jwilder/nginx-proxy:bats`.
Running the test suite
----------------------
Make sure you have Bats installed, then run:
docker build -t jwilder/nginx-proxy:bats .
bats test/

33
test/default-host.bats Normal file
View File

@ -0,0 +1,33 @@
#!/usr/bin/env bats
load test_helpers
function setup {
# make sure to stop any web container before each test so we don't
# have any unexpected contaiener running with VIRTUAL_HOST or VIRUTAL_PORT set
stop_bats_containers web
}
@test "[$TEST_FILE] DEFAULT_HOST=web1.bats" {
SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}-1
# GIVEN a webserver with VIRTUAL_HOST set to web.bats
prepare_web_container bats-web 80 -e VIRTUAL_HOST=web.bats
# WHEN nginx-proxy runs with DEFAULT_HOST set to web.bats
run nginxproxy $SUT_CONTAINER -v /var/run/docker.sock:/tmp/docker.sock:ro -e DEFAULT_HOST=web.bats
assert_success
docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events"
# THEN querying the proxy without Host header → 200
run curl_container $SUT_CONTAINER / --head
assert_output -l 0 $'HTTP/1.1 200 OK\r'
# THEN querying the proxy with any other Host header → 200
run curl_container $SUT_CONTAINER / --head --header "Host: something.I.just.made.up"
assert_output -l 0 $'HTTP/1.1 200 OK\r'
}
@test "[$TEST_FILE] stop all bats containers" {
stop_bats_containers
}

123
test/docker.bats Normal file
View File

@ -0,0 +1,123 @@
#!/usr/bin/env bats
load test_helpers
@test "[$TEST_FILE] start 2 web containers" {
prepare_web_container bats-web1 81 -e VIRTUAL_HOST=web1.bats
prepare_web_container bats-web2 82 -e VIRTUAL_HOST=web2.bats
}
@test "[$TEST_FILE] -v /var/run/docker.sock:/tmp/docker.sock:ro" {
SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}-1
# WHEN nginx-proxy runs on our docker host using the default unix socket
run nginxproxy $SUT_CONTAINER -v /var/run/docker.sock:/tmp/docker.sock:ro
assert_success
docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events"
# THEN
assert_nginxproxy_behaves $SUT_CONTAINER
}
@test "[$TEST_FILE] -v /var/run/docker.sock:/f00.sock:ro -e DOCKER_HOST=unix:///f00.sock" {
SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}-2
# WHEN nginx-proxy runs on our docker host using a custom unix socket
run nginxproxy $SUT_CONTAINER -v /var/run/docker.sock:/f00.sock:ro -e DOCKER_HOST=unix:///f00.sock
assert_success
docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events"
# THEN
assert_nginxproxy_behaves $SUT_CONTAINER
}
@test "[$TEST_FILE] -e DOCKER_HOST=tcp://..." {
SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}-3
# GIVEN a container exposing our docker host over TCP
run docker_tcp bats-docker-tcp
assert_success
sleep 1s
# WHEN nginx-proxy runs on our docker host using tcp to connect to our docker host
run nginxproxy $SUT_CONTAINER -e DOCKER_HOST="tcp://bats-docker-tcp:2375" --link bats-docker-tcp:bats-docker-tcp
assert_success
docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events"
# THEN
assert_nginxproxy_behaves $SUT_CONTAINER
}
@test "[$TEST_FILE] separated containers (nginx + docker-gen + nginx.tmpl)" {
docker_clean bats-nginx
docker_clean bats-docker-gen
# GIVEN a simple nginx container
run docker run -d \
--label bats-type="nginx" \
--name bats-nginx \
-v /etc/nginx/conf.d/ \
-v /etc/nginx/certs/ \
nginx:latest
assert_success
run retry 5 1s docker run --label bats-type="curl" appropriate/curl --silent --fail --head http://$(docker_ip bats-nginx)/
assert_output -l 0 $'HTTP/1.1 200 OK\r'
# WHEN docker-gen runs on our docker host
run docker run -d \
--label bats-type="docker-gen" \
--name bats-docker-gen \
-v /var/run/docker.sock:/tmp/docker.sock:ro \
-v $BATS_TEST_DIRNAME/../nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl:ro \
--volumes-from bats-nginx \
--expose 80 \
jwilder/docker-gen:0.7.1 \
-notify-sighup bats-nginx \
-watch \
-only-exposed \
/etc/docker-gen/templates/nginx.tmpl \
/etc/nginx/conf.d/default.conf
assert_success
docker_wait_for_log bats-docker-gen 9 "Watching docker events"
# Give some time to the docker-gen container to notify bats-nginx so it
# reloads its config
sleep 2s
run docker_running_state bats-nginx
assert_output "true" || {
docker logs bats-docker-gen
false
} >&2
# THEN
assert_nginxproxy_behaves bats-nginx
}
@test "[$TEST_FILE] stop all bats containers" {
stop_bats_containers
}
# $1 nginx-proxy container
function assert_nginxproxy_behaves {
local -r container=$1
# Querying the proxy without Host header → 503
run curl_container $container / --head
assert_output -l 0 $'HTTP/1.1 503 Service Temporarily Unavailable\r'
# Querying the proxy with Host header → 200
run curl_container $container /data --header "Host: web1.bats"
assert_output "answer from port 81"
run curl_container $container /data --header "Host: web2.bats"
assert_output "answer from port 82"
# Querying the proxy with unknown Host header → 503
run curl_container $container /data --header "Host: webFOO.bats" --head
assert_output -l 0 $'HTTP/1.1 503 Service Temporarily Unavailable\r'
}

6
test/lib/README.md Normal file
View File

@ -0,0 +1,6 @@
bats lib
========
found on https://github.com/sstephenson/bats/pull/110
When that pull request will be merged, the `test/lib/bats` won't be necessary anymore.

596
test/lib/bats/batslib.bash Normal file
View File

@ -0,0 +1,596 @@
#
# batslib.bash
# ------------
#
# The Standard Library is a collection of test helpers intended to
# simplify testing. It contains the following types of test helpers.
#
# - Assertions are functions that perform a test and output relevant
# information on failure to help debugging. They return 1 on failure
# and 0 otherwise.
#
# All output is formatted for readability using the functions of
# `output.bash' and sent to the standard error.
#
source "${BATS_LIB}/batslib/output.bash"
########################################################################
# ASSERTIONS
########################################################################
# Fail and display a message. When no parameters are specified, the
# message is read from the standard input. Other functions use this to
# report failure.
#
# Globals:
# none
# Arguments:
# $@ - [=STDIN] message
# Returns:
# 1 - always
# Inputs:
# STDIN - [=$@] message
# Outputs:
# STDERR - message
fail() {
(( $# == 0 )) && batslib_err || batslib_err "$@"
return 1
}
# Fail and display details if the expression evaluates to false. Details
# include the expression, `$status' and `$output'.
#
# NOTE: The expression must be a simple command. Compound commands, such
# as `[[', can be used only when executed with `bash -c'.
#
# Globals:
# status
# output
# Arguments:
# $1 - expression
# Returns:
# 0 - expression evaluates to TRUE
# 1 - otherwise
# Outputs:
# STDERR - details, on failure
assert() {
if ! "$@"; then
{ local -ar single=(
'expression' "$*"
'status' "$status"
)
local -ar may_be_multi=(
'output' "$output"
)
local -ir width="$( batslib_get_max_single_line_key_width \
"${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}"
} | batslib_decorate 'assertion failed' \
| fail
fi
}
# Fail and display details if the expected and actual values do not
# equal. Details include both values.
#
# Globals:
# none
# Arguments:
# $1 - actual value
# $2 - expected value
# Returns:
# 0 - values equal
# 1 - otherwise
# Outputs:
# STDERR - details, on failure
assert_equal() {
if [[ $1 != "$2" ]]; then
batslib_print_kv_single_or_multi 8 \
'expected' "$2" \
'actual' "$1" \
| batslib_decorate 'values do not equal' \
| fail
fi
}
# Fail and display details if `$status' is not 0. Details include
# `$status' and `$output'.
#
# Globals:
# status
# output
# Arguments:
# none
# Returns:
# 0 - `$status' is 0
# 1 - otherwise
# Outputs:
# STDERR - details, on failure
assert_success() {
if (( status != 0 )); then
{ local -ir width=6
batslib_print_kv_single "$width" 'status' "$status"
batslib_print_kv_single_or_multi "$width" 'output' "$output"
} | batslib_decorate 'command failed' \
| fail
fi
}
# Fail and display details if `$status' is 0. Details include `$output'.
#
# Optionally, when the expected status is specified, fail when it does
# not equal `$status'. In this case, details include the expected and
# actual status, and `$output'.
#
# Globals:
# status
# output
# Arguments:
# $1 - [opt] expected status
# Returns:
# 0 - `$status' is not 0, or
# `$status' equals the expected status
# 1 - otherwise
# Outputs:
# STDERR - details, on failure
assert_failure() {
(( $# > 0 )) && local -r expected="$1"
if (( status == 0 )); then
batslib_print_kv_single_or_multi 6 'output' "$output" \
| batslib_decorate 'command succeeded, but it was expected to fail' \
| fail
elif (( $# > 0 )) && (( status != expected )); then
{ local -ir width=8
batslib_print_kv_single "$width" \
'expected' "$expected" \
'actual' "$status"
batslib_print_kv_single_or_multi "$width" \
'output' "$output"
} | batslib_decorate 'command failed as expected, but status differs' \
| fail
fi
}
# Fail and display details if the expected does not match the actual
# output or a fragment of it.
#
# By default, the entire output is matched. The assertion fails if the
# expected output does not equal `$output'. Details include both values.
#
# When `-l <index>' is used, only the <index>-th line is matched. The
# assertion fails if the expected line does not equal
# `${lines[<index>}'. Details include the compared lines and <index>.
#
# When `-l' is used without the <index> argument, the output is searched
# for the expected line. The expected line is matched against each line
# in `${lines[@]}'. If no match is found the assertion fails. Details
# include the expected line and `$output'.
#
# By default, literal matching is performed. Options `-p' and `-r'
# enable partial (i.e. substring) and extended regular expression
# matching, respectively. Specifying an invalid extended regular
# expression with `-r' displays an error.
#
# Options `-p' and `-r' are mutually exclusive. When used
# simultaneously, an error is displayed.
#
# Globals:
# output
# lines
# Options:
# -l <index> - match against the <index>-th element of `${lines[@]}'
# -l - search `${lines[@]}' for the expected line
# -p - partial matching
# -r - extended regular expression matching
# Arguments:
# $1 - expected output
# Returns:
# 0 - expected matches the actual output
# 1 - otherwise
# Outputs:
# STDERR - details, on failure
# error message, on error
assert_output() {
local -i is_match_line=0
local -i is_match_contained=0
local -i is_mode_partial=0
local -i is_mode_regex=0
# Handle options.
while (( $# > 0 )); do
case "$1" in
-l)
if (( $# > 2 )) && [[ $2 =~ ^([0-9]|[1-9][0-9]+)$ ]]; then
is_match_line=1
local -ri idx="$2"
shift
else
is_match_contained=1;
fi
shift
;;
-p) is_mode_partial=1; shift ;;
-r) is_mode_regex=1; shift ;;
--) break ;;
*) break ;;
esac
done
if (( is_match_line )) && (( is_match_contained )); then
echo "\`-l' and \`-l <index>' are mutually exclusive" \
| batslib_decorate 'ERROR: assert_output' \
| fail
return $?
fi
if (( is_mode_partial )) && (( is_mode_regex )); then
echo "\`-p' and \`-r' are mutually exclusive" \
| batslib_decorate 'ERROR: assert_output' \
| fail
return $?
fi
# Arguments.
local -r expected="$1"
if (( is_mode_regex == 1 )) && [[ '' =~ $expected ]] || (( $? == 2 )); then
echo "Invalid extended regular expression: \`$expected'" \
| batslib_decorate 'ERROR: assert_output' \
| fail
return $?
fi
# Matching.
if (( is_match_contained )); then
# Line contained in output.
if (( is_mode_regex )); then
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
[[ ${lines[$idx]} =~ $expected ]] && return 0
done
{ local -ar single=(
'regex' "$expected"
)
local -ar may_be_multi=(
'output' "$output"
)
local -ir width="$( batslib_get_max_single_line_key_width \
"${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}"
} | batslib_decorate 'no output line matches regular expression' \
| fail
elif (( is_mode_partial )); then
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
[[ ${lines[$idx]} == *"$expected"* ]] && return 0
done
{ local -ar single=(
'substring' "$expected"
)
local -ar may_be_multi=(
'output' "$output"
)
local -ir width="$( batslib_get_max_single_line_key_width \
"${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}"
} | batslib_decorate 'no output line contains substring' \
| fail
else
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
[[ ${lines[$idx]} == "$expected" ]] && return 0
done
{ local -ar single=(
'line' "$expected"
)
local -ar may_be_multi=(
'output' "$output"
)
local -ir width="$( batslib_get_max_single_line_key_width \
"${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}"
} | batslib_decorate 'output does not contain line' \
| fail
fi
elif (( is_match_line )); then
# Specific line.
if (( is_mode_regex )); then
if ! [[ ${lines[$idx]} =~ $expected ]]; then
batslib_print_kv_single 5 \
'index' "$idx" \
'regex' "$expected" \
'line' "${lines[$idx]}" \
| batslib_decorate 'regular expression does not match line' \
| fail
fi
elif (( is_mode_partial )); then
if [[ ${lines[$idx]} != *"$expected"* ]]; then
batslib_print_kv_single 9 \
'index' "$idx" \
'substring' "$expected" \
'line' "${lines[$idx]}" \
| batslib_decorate 'line does not contain substring' \
| fail
fi
else
if [[ ${lines[$idx]} != "$expected" ]]; then
batslib_print_kv_single 8 \
'index' "$idx" \
'expected' "$expected" \
'actual' "${lines[$idx]}" \
| batslib_decorate 'line differs' \
| fail
fi
fi
else
# Entire output.
if (( is_mode_regex )); then
if ! [[ $output =~ $expected ]]; then
batslib_print_kv_single_or_multi 6 \
'regex' "$expected" \
'output' "$output" \
| batslib_decorate 'regular expression does not match output' \
| fail
fi
elif (( is_mode_partial )); then
if [[ $output != *"$expected"* ]]; then
batslib_print_kv_single_or_multi 9 \
'substring' "$expected" \
'output' "$output" \
| batslib_decorate 'output does not contain substring' \
| fail
fi
else
if [[ $output != "$expected" ]]; then
batslib_print_kv_single_or_multi 8 \
'expected' "$expected" \
'actual' "$output" \
| batslib_decorate 'output differs' \
| fail
fi
fi
fi
}
# Fail and display details if the unexpected matches the actual output
# or a fragment of it.
#
# By default, the entire output is matched. The assertion fails if the
# unexpected output equals `$output'. Details include `$output'.
#
# When `-l <index>' is used, only the <index>-th line is matched. The
# assertion fails if the unexpected line equals `${lines[<index>}'.
# Details include the compared line and <index>.
#
# When `-l' is used without the <index> argument, the output is searched
# for the unexpected line. The unexpected line is matched against each
# line in `${lines[<index>]}'. If a match is found the assertion fails.
# Details include the unexpected line, the index where it was found and
# `$output' (with the unexpected line highlighted in it if `$output` is
# longer than one line).
#
# By default, literal matching is performed. Options `-p' and `-r'
# enable partial (i.e. substring) and extended regular expression
# matching, respectively. On failure, the substring or the regular
# expression is added to the details (if not already displayed).
# Specifying an invalid extended regular expression with `-r' displays
# an error.
#
# Options `-p' and `-r' are mutually exclusive. When used
# simultaneously, an error is displayed.
#
# Globals:
# output
# lines
# Options:
# -l <index> - match against the <index>-th element of `${lines[@]}'
# -l - search `${lines[@]}' for the unexpected line
# -p - partial matching
# -r - extended regular expression matching
# Arguments:
# $1 - unexpected output
# Returns:
# 0 - unexpected matches the actual output
# 1 - otherwise
# Outputs:
# STDERR - details, on failure
# error message, on error
refute_output() {
local -i is_match_line=0
local -i is_match_contained=0
local -i is_mode_partial=0
local -i is_mode_regex=0
# Handle options.
while (( $# > 0 )); do
case "$1" in
-l)
if (( $# > 2 )) && [[ $2 =~ ^([0-9]|[1-9][0-9]+)$ ]]; then
is_match_line=1
local -ri idx="$2"
shift
else
is_match_contained=1;
fi
shift
;;
-L) is_match_contained=1; shift ;;
-p) is_mode_partial=1; shift ;;
-r) is_mode_regex=1; shift ;;
--) break ;;
*) break ;;
esac
done
if (( is_match_line )) && (( is_match_contained )); then
echo "\`-l' and \`-l <index>' are mutually exclusive" \
| batslib_decorate 'ERROR: refute_output' \
| fail
return $?
fi
if (( is_mode_partial )) && (( is_mode_regex )); then
echo "\`-p' and \`-r' are mutually exclusive" \
| batslib_decorate 'ERROR: refute_output' \
| fail
return $?
fi
# Arguments.
local -r unexpected="$1"
if (( is_mode_regex == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then
echo "Invalid extended regular expression: \`$unexpected'" \
| batslib_decorate 'ERROR: refute_output' \
| fail
return $?
fi
# Matching.
if (( is_match_contained )); then
# Line contained in output.
if (( is_mode_regex )); then
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
if [[ ${lines[$idx]} =~ $unexpected ]]; then
{ local -ar single=(
'regex' "$unexpected"
'index' "$idx"
)
local -a may_be_multi=(
'output' "$output"
)
local -ir width="$( batslib_get_max_single_line_key_width \
"${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
if batslib_is_single_line "${may_be_multi[1]}"; then
batslib_print_kv_single "$width" "${may_be_multi[@]}"
else
may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" \
| batslib_prefix \
| batslib_mark '>' "$idx" )"
batslib_print_kv_multi "${may_be_multi[@]}"
fi
} | batslib_decorate 'no line should match the regular expression' \
| fail
return $?
fi
done
elif (( is_mode_partial )); then
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
if [[ ${lines[$idx]} == *"$unexpected"* ]]; then
{ local -ar single=(
'substring' "$unexpected"
'index' "$idx"
)
local -a may_be_multi=(
'output' "$output"
)
local -ir width="$( batslib_get_max_single_line_key_width \
"${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
if batslib_is_single_line "${may_be_multi[1]}"; then
batslib_print_kv_single "$width" "${may_be_multi[@]}"
else
may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" \
| batslib_prefix \
| batslib_mark '>' "$idx" )"
batslib_print_kv_multi "${may_be_multi[@]}"
fi
} | batslib_decorate 'no line should contain substring' \
| fail
return $?
fi
done
else
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
if [[ ${lines[$idx]} == "$unexpected" ]]; then
{ local -ar single=(
'line' "$unexpected"
'index' "$idx"
)
local -a may_be_multi=(
'output' "$output"
)
local -ir width="$( batslib_get_max_single_line_key_width \
"${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
if batslib_is_single_line "${may_be_multi[1]}"; then
batslib_print_kv_single "$width" "${may_be_multi[@]}"
else
may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" \
| batslib_prefix \
| batslib_mark '>' "$idx" )"
batslib_print_kv_multi "${may_be_multi[@]}"
fi
} | batslib_decorate 'line should not be in output' \
| fail
return $?
fi
done
fi
elif (( is_match_line )); then
# Specific line.
if (( is_mode_regex )); then
if [[ ${lines[$idx]} =~ $unexpected ]] || (( $? == 0 )); then
batslib_print_kv_single 5 \
'index' "$idx" \
'regex' "$unexpected" \
'line' "${lines[$idx]}" \
| batslib_decorate 'regular expression should not match line' \
| fail
fi
elif (( is_mode_partial )); then
if [[ ${lines[$idx]} == *"$unexpected"* ]]; then
batslib_print_kv_single 9 \
'index' "$idx" \
'substring' "$unexpected" \
'line' "${lines[$idx]}" \
| batslib_decorate 'line should not contain substring' \
| fail
fi
else
if [[ ${lines[$idx]} == "$unexpected" ]]; then
batslib_print_kv_single 5 \
'index' "$idx" \
'line' "${lines[$idx]}" \
| batslib_decorate 'line should differ' \
| fail
fi
fi
else
# Entire output.
if (( is_mode_regex )); then
if [[ $output =~ $unexpected ]] || (( $? == 0 )); then
batslib_print_kv_single_or_multi 6 \
'regex' "$unexpected" \
'output' "$output" \
| batslib_decorate 'regular expression should not match output' \
| fail
fi
elif (( is_mode_partial )); then
if [[ $output == *"$unexpected"* ]]; then
batslib_print_kv_single_or_multi 9 \
'substring' "$unexpected" \
'output' "$output" \
| batslib_decorate 'output should not contain substring' \
| fail
fi
else
if [[ $output == "$unexpected" ]]; then
batslib_print_kv_single_or_multi 6 \
'output' "$output" \
| batslib_decorate 'output equals, but it was expected to differ' \
| fail
fi
fi
fi
}

View File

@ -0,0 +1,264 @@
#
# output.bash
# -----------
#
# Private functions implementing output formatting. Used by public
# helper functions.
#
# Print a message to the standard error. When no parameters are
# specified, the message is read from the standard input.
#
# Globals:
# none
# Arguments:
# $@ - [=STDIN] message
# Returns:
# none
# Inputs:
# STDIN - [=$@] message
# Outputs:
# STDERR - message
batslib_err() {
{ if (( $# > 0 )); then
echo "$@"
else
cat -
fi
} >&2
}
# Count the number of lines in the given string.
#
# TODO(ztombol): Fix tests and remove this note after #93 is resolved!
# NOTE: Due to a bug in Bats, `batslib_count_lines "$output"' does not
# give the same result as `${#lines[@]}' when the output contains
# empty lines.
# See PR #93 (https://github.com/sstephenson/bats/pull/93).
#
# Globals:
# none
# Arguments:
# $1 - string
# Returns:
# none
# Outputs:
# STDOUT - number of lines
batslib_count_lines() {
local -i n_lines=0
local line
while IFS='' read -r line || [[ -n $line ]]; do
(( ++n_lines ))
done < <(printf '%s' "$1")
echo "$n_lines"
}
# Determine whether all strings are single-line.
#
# Globals:
# none
# Arguments:
# $@ - strings
# Returns:
# 0 - all strings are single-line
# 1 - otherwise
batslib_is_single_line() {
for string in "$@"; do
(( $(batslib_count_lines "$string") > 1 )) && return 1
done
return 0
}
# Determine the length of the longest key that has a single-line value.
#
# This function is useful in determining the correct width of the key
# column in two-column format when some keys may have multi-line values
# and thus should be excluded.
#
# Globals:
# none
# Arguments:
# $odd - key
# $even - value of the previous key
# Returns:
# none
# Outputs:
# STDOUT - length of longest key
batslib_get_max_single_line_key_width() {
local -i max_len=-1
while (( $# != 0 )); do
local -i key_len="${#1}"
batslib_is_single_line "$2" && (( key_len > max_len )) && max_len="$key_len"
shift 2
done
echo "$max_len"
}
# Print key-value pairs in two-column format.
#
# Keys are displayed in the first column, and their corresponding values
# in the second. To evenly line up values, the key column is fixed-width
# and its width is specified with the first parameter (possibly computed
# using `batslib_get_max_single_line_key_width').
#
# Globals:
# none
# Arguments:
# $1 - width of key column
# $even - key
# $odd - value of the previous key
# Returns:
# none
# Outputs:
# STDOUT - formatted key-value pairs
batslib_print_kv_single() {
local -ir col_width="$1"; shift
while (( $# != 0 )); do
printf '%-*s : %s\n' "$col_width" "$1" "$2"
shift 2
done
}
# Print key-value pairs in multi-line format.
#
# The key is displayed first with the number of lines of its
# corresponding value in parenthesis. Next, starting on the next line,
# the value is displayed. For better readability, it is recommended to
# indent values using `batslib_prefix'.
#
# Globals:
# none
# Arguments:
# $odd - key
# $even - value of the previous key
# Returns:
# none
# Outputs:
# STDOUT - formatted key-value pairs
batslib_print_kv_multi() {
while (( $# != 0 )); do
printf '%s (%d lines):\n' "$1" "$( batslib_count_lines "$2" )"
printf '%s\n' "$2"
shift 2
done
}
# Print all key-value pairs in either two-column or multi-line format
# depending on whether all values are single-line.
#
# If all values are single-line, print all pairs in two-column format
# with the specified key column width (identical to using
# `batslib_print_kv_single').
#
# Otherwise, print all pairs in multi-line format after indenting values
# with two spaces for readability (identical to using `batslib_prefix'
# and `batslib_print_kv_multi')
#
# Globals:
# none
# Arguments:
# $1 - width of key column (for two-column format)
# $even - key
# $odd - value of the previous key
# Returns:
# none
# Outputs:
# STDOUT - formatted key-value pairs
batslib_print_kv_single_or_multi() {
local -ir width="$1"; shift
local -a pairs=( "$@" )
local -a values=()
local -i i
for (( i=1; i < ${#pairs[@]}; i+=2 )); do
values+=( "${pairs[$i]}" )
done
if batslib_is_single_line "${values[@]}"; then
batslib_print_kv_single "$width" "${pairs[@]}"
else
local -i i
for (( i=1; i < ${#pairs[@]}; i+=2 )); do
pairs[$i]="$( batslib_prefix < <(printf '%s' "${pairs[$i]}") )"
done
batslib_print_kv_multi "${pairs[@]}"
fi
}
# Prefix each line read from the standard input with the given string.
#
# Globals:
# none
# Arguments:
# $1 - [= ] prefix string
# Returns:
# none
# Inputs:
# STDIN - lines
# Outputs:
# STDOUT - prefixed lines
batslib_prefix() {
local -r prefix="${1:- }"
local line
while IFS='' read -r line || [[ -n $line ]]; do
printf '%s%s\n' "$prefix" "$line"
done
}
# Mark select lines of the text read from the standard input by
# overwriting their beginning with the given string.
#
# Usually the input is indented by a few spaces using `batslib_prefix'
# first.
#
# Globals:
# none
# Arguments:
# $1 - marking string
# $@ - indices (zero-based) of lines to mark
# Returns:
# none
# Inputs:
# STDIN - lines
# Outputs:
# STDOUT - lines after marking
batslib_mark() {
local -r symbol="$1"; shift
# Sort line numbers.
set -- $( sort -nu <<< "$( printf '%d\n' "$@" )" )
local line
local -i idx=0
while IFS='' read -r line || [[ -n $line ]]; do
if (( ${1:--1} == idx )); then
printf '%s\n' "${symbol}${line:${#symbol}}"
shift
else
printf '%s\n' "$line"
fi
(( ++idx ))
done
}
# Enclose the input text in header and footer lines.
#
# The header contains the given string as title. The output is preceded
# and followed by an additional newline to make it stand out more.
#
# Globals:
# none
# Arguments:
# $1 - title
# Returns:
# none
# Inputs:
# STDIN - text
# Outputs:
# STDOUT - decorated text
batslib_decorate() {
echo
echo "-- $1 --"
cat -
echo '--'
echo
}

View File

@ -0,0 +1,66 @@
## functions to help deal with docker
# Removes container $1
function docker_clean {
docker kill $1 &>/dev/null ||:
sleep .25s
docker rm -vf $1 &>/dev/null ||:
sleep .25s
}
# get the ip of docker container $1
function docker_ip {
docker inspect --format '{{ .NetworkSettings.IPAddress }}' $1
}
# get the ip of docker container $1
function docker_id {
docker inspect --format '{{ .ID }}' $1
}
# get the running state of container $1
# → true/false
# fails if the container does not exist
function docker_running_state {
docker inspect -f {{.State.Running}} $1
}
# get the docker container $1 PID
function docker_pid {
docker inspect --format {{.State.Pid}} $1
}
# asserts logs from container $1 contains $2
function docker_assert_log {
local -r container=$1
shift
run docker logs $container
assert_output -p "$*"
}
# wait for a container to produce a given text in its log
# $1 container
# $2 timeout in second
# $* text to wait for
function docker_wait_for_log {
local -r container=$1
local -ir timeout_sec=$2
shift 2
retry $(( $timeout_sec * 2 )) .5s docker_assert_log $container "$*"
}
# Create a docker container named $1 which exposes the docker host unix
# socket over tcp on port 2375.
#
# $1 container name
function docker_tcp {
local container_name="$1"
docker_clean $container_name
docker run -d \
--label bats-type="socat" \
--name $container_name \
--expose 2375 \
-v /var/run/docker.sock:/var/run/docker.sock \
rancher/socat-docker
docker run --label bats-type="docker" --link "$container_name:docker" docker:1.10 version
}

22
test/lib/helpers.bash Normal file
View File

@ -0,0 +1,22 @@
## add the retry function to bats
# Retry a command $1 times until it succeeds. Wait $2 seconds between retries.
function retry {
local attempts=$1
shift
local delay=$1
shift
local i
for ((i=0; i < attempts; i++)); do
run "$@"
if [ "$status" -eq 0 ]; then
echo "$output"
return 0
fi
sleep $delay
done
echo "Command \"$@\" failed $attempts times. Status: $status. Output: $output" >&2
false
}

View File

@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIID7TCCAtWgAwIBAgIJAOGkf5EnexJVMA0GCSqGSIb3DQEBCwUAMIGMMQswCQYD
VQQGEwJVUzERMA8GA1UECAwIVmlyZ2luaWExDzANBgNVBAcMBlJlc3RvbjERMA8G
A1UECgwIRmFrZSBPcmcxGzAZBgNVBAMMEioubmdpbngtcHJveHkuYmF0czEpMCcG
CSqGSIb3DQEJARYad2VibWFzdGVyQG5naW54LXByb3h5LmJhdHMwHhcNMTYwNDIw
MTUzOTUxWhcNMjYwNDE4MTUzOTUxWjCBjDELMAkGA1UEBhMCVVMxETAPBgNVBAgM
CFZpcmdpbmlhMQ8wDQYDVQQHDAZSZXN0b24xETAPBgNVBAoMCEZha2UgT3JnMRsw
GQYDVQQDDBIqLm5naW54LXByb3h5LmJhdHMxKTAnBgkqhkiG9w0BCQEWGndlYm1h
c3RlckBuZ2lueC1wcm94eS5iYXRzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEA0Amkj3iaQn8Z2CW6n24zSuWu2OoLCkHZAk8eprkI4kKoPBvjusynkm8E
phq65jebToHoldfuQ0wM61DzhD15bHwS3x9CrOVbShsmdnGALz+wdR0/4Likx50I
YZdecTOAlkoZudnX5FZ4ngOxjqcym7p5T8TrSS97a0fx99gitZY0p+Nu2tip4o3t
WBMs+SoPWTlQ1SrSmL8chC8O2knyBl/w1nHmDnMuR6FGcHdhLncApw9t5spgfv7p
OrMF4tQxJQNk10TnflmEMkGmy+pfk2e0cQ1Kwp3Nmzm7ECkggxxyjU3ihKiFK+09
8aSCi7gDAY925+mV6LZ5oLMpO3KJvQIDAQABo1AwTjAdBgNVHQ4EFgQU+NvFo37z
9Dyq8Mu82SPtV7q1gYQwHwYDVR0jBBgwFoAU+NvFo37z9Dyq8Mu82SPtV7q1gYQw
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAI1ityDV0UsCVHSpB2LN+
QXlk8XS0ACIJ8Q0hbOj3BmYrdAVglG4P6upDEueaaxwsaBTagkTP8nxZ9dhfZHyZ
5YLNwYsiG5iqb8e0ecHx3uJT/0YiXn/8rBvxEZna4Fl8seGdp7BjOWUAS2Nv8tn4
EJJvRdfX/O8XgPc95DM4lwQ/dvyWmavMI4lnl0n1IQV9WPGaIQhYPU9WEQK6iMUB
o1kx8YbOJQD0ZBRfqpriNt1/8ylkkSYYav8QT9JFvQFCWEvaX71QF+cuOwC7ZYBH
4ElXwEUrYBHKiPo0q0VsTtMvLh7h/T5czrIhG/NpfVJPtQOk8aVwNScL3/n+TGU8
6g==
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDQCaSPeJpCfxnY
JbqfbjNK5a7Y6gsKQdkCTx6muQjiQqg8G+O6zKeSbwSmGrrmN5tOgeiV1+5DTAzr
UPOEPXlsfBLfH0Ks5VtKGyZ2cYAvP7B1HT/guKTHnQhhl15xM4CWShm52dfkVnie
A7GOpzKbunlPxOtJL3trR/H32CK1ljSn427a2Knije1YEyz5Kg9ZOVDVKtKYvxyE
Lw7aSfIGX/DWceYOcy5HoUZwd2EudwCnD23mymB+/uk6swXi1DElA2TXROd+WYQy
QabL6l+TZ7RxDUrCnc2bObsQKSCDHHKNTeKEqIUr7T3xpIKLuAMBj3bn6ZXotnmg
syk7com9AgMBAAECggEAa7wCp3XqVPNjW+c1/ShhkbDeWmDhtL8i9aopkmeSbTHd
07sRtQQU56Vsf+Sp010KpZ5q52Z6cglpS1eRtHLtdbvPPhL/QXBJVVg4E/B1VIKk
DBJIqUSVuPXeiEOOWgs01R+ssO1ae1o4foQlKF33vGPWPPQacL0RKh6I9TPNzcD7
n4rujlHk72N/bNydyK2rnyKB4vAI5TbZPLps+Xe123CmgZnW3JClcWV9B4foRmiu
a5Iq1WYAK2GYKbYwgqDRyYBC27m91a7U31pE4GQD+xQdlz6kcOlCU5hAcPK3h7j0
fLQqn8g+YAtc0nBKKB4NZe3QEzTiVMorT0VitxI71QKBgQDnirardZaXOFzYGzB3
j+FGB9BUW54hnHr5BxOYrfmEJ5umJjJWaGupfYrQsPArrJP1//WbqVZIPvdQParD
mQhLmSp1r/VNzGB6pISmzU1ZGDHsmBxYseh366om5YBQUFU2vmbil9VkrkM4fsJG
tcS9V/nVY/EM7Yp3PzjfLlhC1wKBgQDmA1YJmnZvIbLp3PoKqM69QiCLKztVm7nX
xpu3b3qbXEzXkt2sP5PHmr+s13hOPQFKRJ2hk4UN9WqpnFoHw5E5eWWhSa/peUZm
r10Y5XspiFtRHHiu6ABXB49eB4fen+vHEZHKyRJ4rFthKjjBHdNPC8bmwnT3jE85
/8a26FLZiwKBgQDXEi8JZslBn9YF2oOTm28KCLoHka551AsaA+u892T8z3mxxGsf
fhD7N6TYonIEb2Jkr6OpOortwqcgvpc+5oghCJ27AX2fDUdUxDp/YdYF+wZsmQJD
lMW1lo7PYIBmmaf9mLCiq5xIz+GauYul+LNNmUl0YEgI1SC4EV63WCodswKBgDMX
GJxHd/kVViVGFTAa8NjvAEWJU8OfNHduQRZMp8IsjVDw6VYiRRP4Fo0wyyMtv8Sc
WxsRpmNEWO3VsdW5pd9LTLy3nmBQtMeIOjiWeHXwOMBaf5/yHmk2X6z2JULY6Mkt
6OFPKlAtkJqTg0m58z7Ckeqd1NdLjimG27+y+PwjAoGAFt0cbC1Ust2BE6YEspSX
ofpAnJsyKrbF9iVUyXDUP99sdqYQfPJ5uqPGkP59lJGkTLtebuitqi6FCyrsT6Fq
AWLiExbqebAqcuAZw2S+iuK27S4rrkjVGF53J7vH3rOzCBUXaRx6GKfTjUqedHdg
9Kw+LP6IFnMTb+EGLo+GqHs=
-----END PRIVATE KEY-----

43
test/multiple-hosts.bats Normal file
View File

@ -0,0 +1,43 @@
#!/usr/bin/env bats
load test_helpers
SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}
function setup {
# make sure to stop any web container before each test so we don't
# have any unexpected contaiener running with VIRTUAL_HOST or VIRUTAL_PORT set
stop_bats_containers web
}
@test "[$TEST_FILE] start a nginx-proxy container" {
run nginxproxy $SUT_CONTAINER -v /var/run/docker.sock:/tmp/docker.sock:ro
assert_success
docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events"
}
@test "[$TEST_FILE] nginx-proxy forwards requests for 2 hosts" {
# WHEN a container runs a web server with VIRTUAL_HOST set for multiple hosts
prepare_web_container bats-multiple-hosts-1 80 -e VIRTUAL_HOST=multiple-hosts-1-A.bats,multiple-hosts-1-B.bats
dockergen_wait_for_event $SUT_CONTAINER start bats-multiple-hosts-1
sleep 1
# THEN querying the proxy without Host header → 503
run curl_container $SUT_CONTAINER / --head
assert_output -l 0 $'HTTP/1.1 503 Service Temporarily Unavailable\r'
# THEN querying the proxy with unknown Host header → 503
run curl_container $SUT_CONTAINER /data --header "Host: webFOO.bats" --head
assert_output -l 0 $'HTTP/1.1 503 Service Temporarily Unavailable\r'
# THEN
run curl_container $SUT_CONTAINER /data --header 'Host: multiple-hosts-1-A.bats'
assert_output "answer from port 80"
# THEN
run curl_container $SUT_CONTAINER /data --header 'Host: multiple-hosts-1-B.bats'
assert_output "answer from port 80"
}
@test "[$TEST_FILE] stop all bats containers" {
stop_bats_containers
}

64
test/multiple-ports.bats Normal file
View File

@ -0,0 +1,64 @@
#!/usr/bin/env bats
load test_helpers
SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}
function setup {
# make sure to stop any web container before each test so we don't
# have any unexpected contaiener running with VIRTUAL_HOST or VIRUTAL_PORT set
stop_bats_containers web
}
@test "[$TEST_FILE] start a nginx-proxy container" {
# GIVEN nginx-proxy
run nginxproxy $SUT_CONTAINER -v /var/run/docker.sock:/tmp/docker.sock:ro
assert_success
docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events"
}
@test "[$TEST_FILE] nginx-proxy defaults to the service running on port 80" {
# WHEN
prepare_web_container bats-web-${TEST_FILE}-1 "80 90" -e VIRTUAL_HOST=web.bats
dockergen_wait_for_event $SUT_CONTAINER start bats-web-${TEST_FILE}-1
sleep 1
# THEN
assert_response_is_from_port 80
}
@test "[$TEST_FILE] VIRTUAL_PORT=90 while port 80 is also exposed" {
# GIVEN
prepare_web_container bats-web-${TEST_FILE}-2 "80 90" -e VIRTUAL_HOST=web.bats -e VIRTUAL_PORT=90
dockergen_wait_for_event $SUT_CONTAINER start bats-web-${TEST_FILE}-2
sleep 1
# THEN
assert_response_is_from_port 90
}
@test "[$TEST_FILE] single exposed port != 80" {
# GIVEN
prepare_web_container bats-web-${TEST_FILE}-3 1234 -e VIRTUAL_HOST=web.bats
dockergen_wait_for_event $SUT_CONTAINER start bats-web-${TEST_FILE}-3
sleep 1
# THEN
assert_response_is_from_port 1234
}
@test "[$TEST_FILE] stop all bats containers" {
stop_bats_containers
}
# assert querying nginx-proxy provides a response from the expected port of the web container
# $1 port we are expecting an response from
function assert_response_is_from_port {
local -r port=$1
run curl_container $SUT_CONTAINER /data --header "Host: web.bats"
assert_output "answer from port $port"
}

117
test/ssl.bats Normal file
View File

@ -0,0 +1,117 @@
#!/usr/bin/env bats
load test_helpers
SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}
function setup {
# make sure to stop any web container before each test so we don't
# have any unexpected contaiener running with VIRTUAL_HOST or VIRUTAL_PORT set
stop_bats_containers web
}
@test "[$TEST_FILE] start a nginx-proxy container" {
run nginxproxy $SUT_CONTAINER -v /var/run/docker.sock:/tmp/docker.sock:ro -v ${DIR}/lib/ssl:/etc/nginx/certs:ro
assert_success
docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events"
}
@test "[$TEST_FILE] test SSL for VIRTUAL_HOST=*.nginx-proxy.bats" {
# WHEN
prepare_web_container bats-ssl-hosts-1 "80 443" \
-e VIRTUAL_HOST=*.nginx-proxy.bats \
-e CERT_NAME=nginx-proxy.bats
dockergen_wait_for_event $SUT_CONTAINER start bats-ssl-hosts-1
sleep 1
# THEN
assert_301 test.nginx-proxy.bats
assert_200_https test.nginx-proxy.bats
}
@test "[$TEST_FILE] test HTTPS_METHOD=nohttp" {
# WHEN
prepare_web_container bats-ssl-hosts-2 "80 443" \
-e VIRTUAL_HOST=*.nginx-proxy.bats \
-e CERT_NAME=nginx-proxy.bats \
-e HTTPS_METHOD=nohttp
dockergen_wait_for_event $SUT_CONTAINER start bats-ssl-hosts-2
sleep 1
# THEN
assert_503 test.nginx-proxy.bats
assert_200_https test.nginx-proxy.bats
}
@test "[$TEST_FILE] test HTTPS_METHOD=noredirect" {
# WHEN
prepare_web_container bats-ssl-hosts-3 "80 443" \
-e VIRTUAL_HOST=*.nginx-proxy.bats \
-e CERT_NAME=nginx-proxy.bats \
-e HTTPS_METHOD=noredirect
dockergen_wait_for_event $SUT_CONTAINER start bats-ssl-hosts-3
sleep 1
# THEN
assert_200 test.nginx-proxy.bats
assert_200_https test.nginx-proxy.bats
}
@test "[$TEST_FILE] stop all bats containers" {
stop_bats_containers
}
# assert that querying nginx-proxy with the given Host header produces a `HTTP 200` response
# $1 Host HTTP header to use when querying nginx-proxy
function assert_200 {
local -r host=$1
run curl_container $SUT_CONTAINER / --head --header "Host: $host"
assert_output -l 0 $'HTTP/1.1 200 OK\r'
}
# assert that querying nginx-proxy with the given Host header produces a `HTTP 503` response
# $1 Host HTTP header to use when querying nginx-proxy
function assert_503 {
local -r host=$1
run curl_container $SUT_CONTAINER / --head --header "Host: $host"
assert_output -l 0 $'HTTP/1.1 503 Service Temporarily Unavailable\r'
}
# assert that querying nginx-proxy with the given Host header produces a `HTTP 503` response
# $1 Host HTTP header to use when querying nginx-proxy
function assert_301 {
local -r host=$1
run curl_container $SUT_CONTAINER / --head --header "Host: $host"
assert_output -l 0 $'HTTP/1.1 301 Moved Permanently\r'
}
# assert that querying nginx-proxy with the given Host header produces a `HTTP 200` response
# $1 Host HTTP header to use when querying nginx-proxy
function assert_200_https {
local -r host=$1
run curl_container_https $SUT_CONTAINER / --head --header "Host: $host"
assert_output -l 0 $'HTTP/1.1 200 OK\r'
}
# assert that querying nginx-proxy with the given Host header produces a `HTTP 503` response
# $1 Host HTTP header to use when querying nginx-proxy
function assert_503_https {
local -r host=$1
run curl_container_https $SUT_CONTAINER / --head --header "Host: $host"
assert_output -l 0 $'HTTP/1.1 503 Service Temporarily Unavailable\r'
}
# assert that querying nginx-proxy with the given Host header produces a `HTTP 503` response
# $1 Host HTTP header to use when querying nginx-proxy
function assert_301_https {
local -r host=$1
run curl_container_https $SUT_CONTAINER / --head --header "Host: $host"
assert_output -l 0 $'HTTP/1.1 301 Moved Permanently\r'
}

184
test/test_helpers.bash Normal file
View File

@ -0,0 +1,184 @@
# Test if requirements are met
(
type docker &>/dev/null || ( echo "docker is not available"; exit 1 )
)>&2
# set a few global variables
SUT_IMAGE=jwilder/nginx-proxy:bats
TEST_FILE=$(basename $BATS_TEST_FILENAME .bats)
# load the Bats stdlib (see https://github.com/sstephenson/bats/pull/110)
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
export BATS_LIB="${DIR}/lib/bats"
load "${BATS_LIB}/batslib.bash"
# load additional bats helpers
load ${DIR}/lib/helpers.bash
load ${DIR}/lib/docker_helpers.bash
# Define functions specific to our test suite
# run the SUT docker container
# and makes sure it remains started
# and displays the nginx-proxy start logs
#
# $1 container name
# $@ other options for the `docker run` command
function nginxproxy {
local -r container_name=$1
shift
docker_clean $container_name \
&& docker run -d \
--label bats-type="nginx-proxy" \
--name $container_name \
"$@" \
$SUT_IMAGE \
&& wait_for_nginxproxy_container_to_start $container_name \
&& docker logs $container_name
}
# wait until the nginx-proxy container is ready to operate
#
# $1 container name
function wait_for_nginxproxy_container_to_start {
local -r container_name=$1
sleep .5s # give time to eventually fail to initialize
function is_running {
run docker_running_state $container_name
assert_output "true"
}
retry 3 1 is_running
}
# Send a HTTP request to container $1 for path $2 and
# Additional curl options can be passed as $@
#
# $1 container name
# $2 HTTP path to query
# $@ additional options to pass to the curl command
function curl_container {
local -r container=$1
local -r path=$2
shift 2
docker run --label bats-type="curl" appropriate/curl --silent \
--connect-timeout 5 \
--max-time 20 \
"$@" \
http://$(docker_ip $container)${path}
}
# Send a HTTPS request to container $1 for path $2 and
# Additional curl options can be passed as $@
#
# $1 container name
# $2 HTTPS path to query
# $@ additional options to pass to the curl command
function curl_container_https {
local -r container=$1
local -r path=$2
shift 2
docker run --label bats-type="curl" appropriate/curl --silent \
--connect-timeout 5 \
--max-time 20 \
--insecure \
"$@" \
https://$(docker_ip $container)${path}
}
# start a container running (one or multiple) webservers listening on given ports
#
# $1 container name
# $2 container port(s). If multiple ports, provide them as a string: "80 90" with a space as a separator
# $@ `docker run` additional options
function prepare_web_container {
local -r container_name=$1
local -r ports=$2
shift 2
local -r options="$@"
local expose_option=""
IFS=$' \t\n' # See https://github.com/sstephenson/bats/issues/89
for port in $ports; do
expose_option="${expose_option}--expose=$port "
done
( # used for debugging purpose. Will be display if test fails
echo "container_name: $container_name"
echo "ports: $ports"
echo "options: $options"
echo "expose_option: $expose_option"
)>&2
docker_clean $container_name
# GIVEN a container exposing 1 webserver on ports 1234
run docker run -d \
--label bats-type="web" \
--name $container_name \
$expose_option \
-w /var/www/ \
$options \
-e PYTHON_PORTS="$ports" \
python:3 bash -c "
trap '[ \${#PIDS[@]} -gt 0 ] && kill -TERM \${PIDS[@]}' TERM
declare -a PIDS
for port in \$PYTHON_PORTS; do
echo starting a web server listening on port \$port;
mkdir /var/www/\$port
cd /var/www/\$port
echo \"answer from port \$port\" > data
python -m http.server \$port &
PIDS+=(\$!)
done
wait \${PIDS[@]}
trap - TERM
wait \${PIDS[@]}
"
assert_success
# THEN querying directly port works
IFS=$' \t\n' # See https://github.com/sstephenson/bats/issues/89
for port in $ports; do
run retry 5 1s docker run --label bats-type="curl" appropriate/curl --silent --fail http://$(docker_ip $container_name):$port/data
assert_output "answer from port $port"
done
}
# stop all containers with the "bats-type" label (matching the optionally supplied value)
#
# $1 optional label value
function stop_bats_containers {
local -r value=$1
if [ -z "$value" ]; then
CIDS=( $(docker ps -q --filter "label=bats-type") )
else
CIDS=( $(docker ps -q --filter "label=bats-type=$value") )
fi
if [ ${#CIDS[@]} -gt 0 ]; then
docker stop ${CIDS[@]} >&2
fi
}
# wait for a docker-gen container to receive a specified event from a
# container with the specified ID/name
#
# $1 docker-gen container name
# $2 event
# $3 ID/name of container to receive event from
function dockergen_wait_for_event {
local -r container=$1
local -r event=$2
local -r other=$3
local -r did=$(docker_id "$other")
docker_wait_for_log "$container" 9 "Received event $event for container ${did:0:12}"
}

78
test/wildcard-hosts.bats Normal file
View File

@ -0,0 +1,78 @@
#!/usr/bin/env bats
load test_helpers
SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE}
function setup {
# make sure to stop any web container before each test so we don't
# have any unexpected contaiener running with VIRTUAL_HOST or VIRUTAL_PORT set
stop_bats_containers web
}
@test "[$TEST_FILE] start a nginx-proxy container" {
# GIVEN
run nginxproxy $SUT_CONTAINER -v /var/run/docker.sock:/tmp/docker.sock:ro
assert_success
docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events"
}
@test "[$TEST_FILE] VIRTUAL_HOST=*.wildcard.bats" {
# WHEN
prepare_web_container bats-wildcard-hosts-1 80 -e VIRTUAL_HOST=*.wildcard.bats
dockergen_wait_for_event $SUT_CONTAINER start bats-wildcard-hosts-1
sleep 1
# THEN
assert_200 f00.wildcard.bats
assert_200 bar.wildcard.bats
assert_503 unexpected.host.bats
}
@test "[$TEST_FILE] VIRTUAL_HOST=wildcard.bats.*" {
# WHEN
prepare_web_container bats-wildcard-hosts-2 80 -e VIRTUAL_HOST=wildcard.bats.*
dockergen_wait_for_event $SUT_CONTAINER start bats-wildcard-hosts-2
sleep 1
# THEN
assert_200 wildcard.bats.f00
assert_200 wildcard.bats.bar
assert_503 unexpected.host.bats
}
@test "[$TEST_FILE] VIRTUAL_HOST=~^foo\.bar\..*\.bats" {
# WHEN
prepare_web_container bats-wildcard-hosts-2 80 -e VIRTUAL_HOST=~^foo\.bar\..*\.bats
dockergen_wait_for_event $SUT_CONTAINER start bats-wildcard-hosts-2
sleep 1
# THEN
assert_200 foo.bar.whatever.bats
assert_200 foo.bar.why.not.bats
assert_503 unexpected.host.bats
}
@test "[$TEST_FILE] stop all bats containers" {
stop_bats_containers
}
# assert that querying nginx-proxy with the given Host header produces a `HTTP 200` response
# $1 Host HTTP header to use when querying nginx-proxy
function assert_200 {
local -r host=$1
run curl_container $SUT_CONTAINER / --head --header "Host: $host"
assert_output -l 0 $'HTTP/1.1 200 OK\r'
}
# assert that querying nginx-proxy with the given Host header produces a `HTTP 503` response
# $1 Host HTTP header to use when querying nginx-proxy
function assert_503 {
local -r host=$1
run curl_container $SUT_CONTAINER / --head --header "Host: $host"
assert_output -l 0 $'HTTP/1.1 503 Service Temporarily Unavailable\r'
}