Merge 'v1.4.2' into master
This commit is contained in:
commit
2070aa9443
16 changed files with 304 additions and 87 deletions
21
.github/PULL_REQUEST_TEMPLATE.md
vendored
21
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -16,8 +16,21 @@ HOW TO WRITE A GOOD PULL REQUEST?
|
||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
### Description
|
### What does this PR do?
|
||||||
|
|
||||||
<!--
|
<!-- A brief description of the change being made with this pull request. -->
|
||||||
Briefly describe the pull request in a few paragraphs.
|
|
||||||
-->
|
|
||||||
|
### Motivation
|
||||||
|
|
||||||
|
<!-- What inspired you to submit this pull request? -->
|
||||||
|
|
||||||
|
|
||||||
|
### More
|
||||||
|
|
||||||
|
- [ ] Added/updated tests
|
||||||
|
- [ ] Added/updated documentation
|
||||||
|
|
||||||
|
### Additional Notes
|
||||||
|
|
||||||
|
<!-- Anything else we should know when reviewing? -->
|
||||||
|
|
18
CHANGELOG.md
18
CHANGELOG.md
|
@ -1,5 +1,23 @@
|
||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## [v1.4.2](https://github.com/containous/traefik/tree/v1.4.2) (2017-11-02)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v1.4.1...v1.4.2)
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[cluster]** Fix datastore corruption on reload due to shrinking config size ([#2340](https://github.com/containous/traefik/pull/2340) by [else](https://github.com/else))
|
||||||
|
- **[docker,docker/swarm]** Make frontend names differents for similar routes ([#2338](https://github.com/containous/traefik/pull/2338) by [nmengin](https://github.com/nmengin))
|
||||||
|
- **[docker]** Fix IP address when Docker container network mode is container ([#2331](https://github.com/containous/traefik/pull/2331) by [nmengin](https://github.com/nmengin))
|
||||||
|
- **[docker]** Make the traefik.port label optional when using service labels in Docker containers. ([#2330](https://github.com/containous/traefik/pull/2330) by [nmengin](https://github.com/nmengin))
|
||||||
|
- **[docker]** Add unique ID to Docker services replicas ([#2314](https://github.com/containous/traefik/pull/2314) by [nmengin](https://github.com/nmengin))
|
||||||
|
- **[marathon]** Missing Backend key in configuration when application has no tasks ([#2333](https://github.com/containous/traefik/pull/2333) by [aantono](https://github.com/aantono))
|
||||||
|
- Remove hardcoded runtime.GOMAXPROCS. ([#2317](https://github.com/containous/traefik/pull/2317) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- **[k8s]** fixed dead link in kubernetes backend config docs ([#2337](https://github.com/containous/traefik/pull/2337) by [perplexa](https://github.com/perplexa))
|
||||||
|
- **[k8s]** Fix the k8s docs example deployment yaml ([#2308](https://github.com/containous/traefik/pull/2308) by [gnur](https://github.com/gnur))
|
||||||
|
- Minor grammar change ([#2350](https://github.com/containous/traefik/pull/2350) by [haxorjim](https://github.com/haxorjim))
|
||||||
|
- Minor typo ([#2343](https://github.com/containous/traefik/pull/2343) by [burningTyger](https://github.com/burningTyger))
|
||||||
|
|
||||||
## [v1.4.1](https://github.com/containous/traefik/tree/v1.4.1) (2017-10-24)
|
## [v1.4.1](https://github.com/containous/traefik/tree/v1.4.1) (2017-10-24)
|
||||||
[All Commits](https://github.com/containous/traefik/compare/v1.4.0...v1.4.1)
|
[All Commits](https://github.com/containous/traefik/compare/v1.4.0...v1.4.1)
|
||||||
|
|
||||||
|
|
|
@ -119,20 +119,9 @@ func (d *Datastore) watchChanges() error {
|
||||||
|
|
||||||
func (d *Datastore) reload() error {
|
func (d *Datastore) reload() error {
|
||||||
log.Debug("Datastore reload")
|
log.Debug("Datastore reload")
|
||||||
d.localLock.Lock()
|
_, err := d.Load()
|
||||||
err := d.kv.LoadConfig(d.meta)
|
|
||||||
if err != nil {
|
|
||||||
d.localLock.Unlock()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = d.meta.unmarshall()
|
|
||||||
if err != nil {
|
|
||||||
d.localLock.Unlock()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
d.localLock.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Begin creates a transaction with the KV store.
|
// Begin creates a transaction with the KV store.
|
||||||
func (d *Datastore) Begin() (Transaction, Object, error) {
|
func (d *Datastore) Begin() (Transaction, Object, error) {
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -34,8 +33,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
|
||||||
|
|
||||||
//traefik config inits
|
//traefik config inits
|
||||||
traefikConfiguration := NewTraefikConfiguration()
|
traefikConfiguration := NewTraefikConfiguration()
|
||||||
traefikPointersConfiguration := NewTraefikDefaultPointersConfiguration()
|
traefikPointersConfiguration := NewTraefikDefaultPointersConfiguration()
|
||||||
|
|
|
@ -189,6 +189,12 @@ Services labels can be used for overriding default behaviour
|
||||||
| `traefik.<service-name>.frontend.priority` | Overrides `traefik.frontend.priority`. |
|
| `traefik.<service-name>.frontend.priority` | Overrides `traefik.frontend.priority`. |
|
||||||
| `traefik.<service-name>.frontend.rule` | Overrides `traefik.frontend.rule`. |
|
| `traefik.<service-name>.frontend.rule` | Overrides `traefik.frontend.rule`. |
|
||||||
|
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
if a label is defined both as a `container label` and a `service label` (for example `traefik.<service-name>.port=PORT` and `traefik.port=PORT` ), the `service label` is used to defined the `<service-name>` property (`port` in the example).
|
||||||
|
It's possible to mix `container labels` and `service labels`, in this case `container labels` are used as default value for missing `service labels` but no frontends are going to be created with the `container labels`.
|
||||||
|
More details in this [example](/user-guide/docker-and-lets-encrypt/#labels).
|
||||||
|
|
||||||
!!! warning
|
!!! warning
|
||||||
when running inside a container, Træfik will need network access through:
|
when running inside a container, Træfik will need network access through:
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
Træfik can be configured to use Kubernetes Ingress as a backend configuration.
|
Træfik can be configured to use Kubernetes Ingress as a backend configuration.
|
||||||
|
|
||||||
See also [Kubernetes user guide](/user-guide/kubernetes).
|
See also [Kubernetes user guide](/docs/user-guide/kubernetes).
|
||||||
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
|
@ -123,10 +123,10 @@ Otherwise, the response from the auth server is returned.
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[entryPoints]
|
[entryPoints]
|
||||||
[entrypoints.http]
|
[entryPoints.http]
|
||||||
# ...
|
# ...
|
||||||
# To enable forward auth on an entrypoint
|
# To enable forward auth on an entrypoint
|
||||||
[entrypoints.http.auth.forward]
|
[entryPoints.http.auth.forward]
|
||||||
address = "https://authserver.com/auth"
|
address = "https://authserver.com/auth"
|
||||||
|
|
||||||
# Trust existing X-Forwarded-* headers.
|
# Trust existing X-Forwarded-* headers.
|
||||||
|
@ -141,7 +141,7 @@ Otherwise, the response from the auth server is returned.
|
||||||
#
|
#
|
||||||
# Optional
|
# Optional
|
||||||
#
|
#
|
||||||
[entrypoints.http.auth.forward.tls]
|
[entryPoints.http.auth.forward.tls]
|
||||||
cert = "authserver.crt"
|
cert = "authserver.crt"
|
||||||
key = "authserver.key"
|
key = "authserver.key"
|
||||||
```
|
```
|
||||||
|
@ -226,7 +226,7 @@ Only IPs in `trustedIPs` will lead to remote client address replacement: you sho
|
||||||
|
|
||||||
## Forwarded Header
|
## Forwarded Header
|
||||||
|
|
||||||
Only IPs in `trustedIPs` will be authorize to trust the client forwarded headers (`X-Forwarded-*`).
|
Only IPs in `trustedIPs` will be authorized to trust the client forwarded headers (`X-Forwarded-*`).
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[entryPoints]
|
[entryPoints]
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
# Docker & Traefik
|
# Docker & Traefik
|
||||||
|
|
||||||
In this use case, we want to use Traefik as a _layer-7_ load balancer with SSL termination for a set of micro-services used to run a web application.
|
In this use case, we want to use Træfik as a _layer-7_ load balancer with SSL termination for a set of micro-services used to run a web application.
|
||||||
|
|
||||||
We also want to automatically _discover any services_ on the Docker host and let Traefik reconfigure itself automatically when containers get created (or shut down) so HTTP traffic can be routed accordingly.
|
We also want to automatically _discover any services_ on the Docker host and let Træfik reconfigure itself automatically when containers get created (or shut down) so HTTP traffic can be routed accordingly.
|
||||||
|
|
||||||
In addition, we want to use Let's Encrypt to automatically generate and renew SSL certificates per hostname.
|
In addition, we want to use Let's Encrypt to automatically generate and renew SSL certificates per hostname.
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ In real-life, you'll want to use your own domain and have the DNS configured acc
|
||||||
Docker containers can only communicate with each other over TCP when they share at least one network.
|
Docker containers can only communicate with each other over TCP when they share at least one network.
|
||||||
This makes sense from a topological point of view in the context of networking, since Docker under the hood creates IPTable rules so containers can't reach other containers _unless you'd want to_.
|
This makes sense from a topological point of view in the context of networking, since Docker under the hood creates IPTable rules so containers can't reach other containers _unless you'd want to_.
|
||||||
|
|
||||||
In this example, we're going to use a single network called `web` where all containers that are handling HTTP traffic (including Traefik) will reside in.
|
In this example, we're going to use a single network called `web` where all containers that are handling HTTP traffic (including Træfik) will reside in.
|
||||||
|
|
||||||
On the Docker host, run the following command:
|
On the Docker host, run the following command:
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ On the Docker host, run the following command:
|
||||||
docker network create web
|
docker network create web
|
||||||
```
|
```
|
||||||
|
|
||||||
Now, let's create a directory on the server where we will configure the rest of Traefik:
|
Now, let's create a directory on the server where we will configure the rest of Træfik:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
mkdir -p /opt/traefik
|
mkdir -p /opt/traefik
|
||||||
|
@ -41,7 +41,7 @@ touch /opt/traefik/acme.json && chmod 600 /opt/traefik/acme.json
|
||||||
touch /opt/traefik/traefik.toml
|
touch /opt/traefik/traefik.toml
|
||||||
```
|
```
|
||||||
|
|
||||||
The `docker-compose.yml` file will provide us with a simple, consistent and more importantly, a deterministic way to create Traefik.
|
The `docker-compose.yml` file will provide us with a simple, consistent and more importantly, a deterministic way to create Træfik.
|
||||||
|
|
||||||
The contents of the file is as follows:
|
The contents of the file is as follows:
|
||||||
|
|
||||||
|
@ -69,12 +69,12 @@ networks:
|
||||||
```
|
```
|
||||||
|
|
||||||
As you can see, we're mounting the `traefik.toml` file as well as the (empty) `acme.json` file in the container.
|
As you can see, we're mounting the `traefik.toml` file as well as the (empty) `acme.json` file in the container.
|
||||||
Also, we're mounting the `/var/run/docker.sock` Docker socket in the container as well, so Traefik can listen to Docker events and reconfigure it's own internal configuration when containers are created (or shut down).
|
Also, we're mounting the `/var/run/docker.sock` Docker socket in the container as well, so Træfik can listen to Docker events and reconfigure it's own internal configuration when containers are created (or shut down).
|
||||||
Also, we're making sure the container is automatically restarted by the Docker engine in case of problems (or: if the server is rebooted).
|
Also, we're making sure the container is automatically restarted by the Docker engine in case of problems (or: if the server is rebooted).
|
||||||
We're publishing the default HTTP ports `80` and `443` on the host, and making sure the container is placed within the `web` network we've created earlier on.
|
We're publishing the default HTTP ports `80` and `443` on the host, and making sure the container is placed within the `web` network we've created earlier on.
|
||||||
Finally, we're giving this container a static name called `traefik`.
|
Finally, we're giving this container a static name called `traefik`.
|
||||||
|
|
||||||
Let's take a look at a simple `traefik.toml` configuration as well before we'll create the Traefik container:
|
Let's take a look at a simple `traefik.toml` configuration as well before we'll create the Træfik container:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
debug = false
|
debug = false
|
||||||
|
@ -109,17 +109,17 @@ OnHostRule = true
|
||||||
This is the minimum configuration required to do the following:
|
This is the minimum configuration required to do the following:
|
||||||
|
|
||||||
- Log `ERROR`-level messages (or more severe) to the console, but silence `DEBUG`-level messagse
|
- Log `ERROR`-level messages (or more severe) to the console, but silence `DEBUG`-level messagse
|
||||||
- Check for new versions of Traefik periodically
|
- Check for new versions of Træfik periodically
|
||||||
- Create two entry points, namely an `HTTP` endpoint on port `80`, and an `HTTPS` endpoint on port `443` where all incoming traffic on port `80` will immediately get redirected to `HTTPS`.
|
- Create two entry points, namely an `HTTP` endpoint on port `80`, and an `HTTPS` endpoint on port `443` where all incoming traffic on port `80` will immediately get redirected to `HTTPS`.
|
||||||
- Enable the Docker configuration backend and listen for container events on the Docker unix socket we've mounted earlier. However, **new containers will not be exposed by Traefik by default, we'll get into this in a bit!**
|
- Enable the Docker configuration backend and listen for container events on the Docker unix socket we've mounted earlier. However, **new containers will not be exposed by Træfik by default, we'll get into this in a bit!**
|
||||||
- Enable automatic request and configuration of SSL certificates using Let's Encrypt.
|
- Enable automatic request and configuration of SSL certificates using Let's Encrypt.
|
||||||
These certificates will be stored in the `acme.json` file, which you can back-up yourself and store off-premises.
|
These certificates will be stored in the `acme.json` file, which you can back-up yourself and store off-premises.
|
||||||
|
|
||||||
Alright, let's boot the container. From the `/opt/traefik` directory, run `docker-compose up -d` which will create and start the Traefik container.
|
Alright, let's boot the container. From the `/opt/traefik` directory, run `docker-compose up -d` which will create and start the Træfik container.
|
||||||
|
|
||||||
## Exposing Web Services to the Outside World
|
## Exposing Web Services to the Outside World
|
||||||
|
|
||||||
Now that we've fully configured and started Traefik, it's time to get our applications running!
|
Now that we've fully configured and started Træfik, it's time to get our applications running!
|
||||||
|
|
||||||
Let's take a simple example of a micro-service project consisting of various services, where some will be exposed to the outside world and some will not.
|
Let's take a simple example of a micro-service project consisting of various services, where some will be exposed to the outside world and some will not.
|
||||||
|
|
||||||
|
@ -148,6 +148,10 @@ services:
|
||||||
- "traefik.frontend.rule=Host:app.my-awesome-app.org"
|
- "traefik.frontend.rule=Host:app.my-awesome-app.org"
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.port=9000"
|
- "traefik.port=9000"
|
||||||
|
- "traefik.default.protocol=http"
|
||||||
|
- "traefik.admin.frontend.rule=Host:admin-app.my-awesome-app.org"
|
||||||
|
- "traefik.admin.protocol=https"
|
||||||
|
- "traefik.admin.port=9443"
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: my-docker-registry.com/back-end/5.7
|
image: my-docker-registry.com/back-end/5.7
|
||||||
|
@ -190,10 +194,10 @@ Since the `traefik` container we've created and started earlier is also attached
|
||||||
|
|
||||||
### Labels
|
### Labels
|
||||||
|
|
||||||
As mentioned earlier, we don't want containers exposed automatically by Traefik.
|
As mentioned earlier, we don't want containers exposed automatically by Træfik.
|
||||||
|
|
||||||
The reason behind this is simple: we want to have control over this process ourselves.
|
The reason behind this is simple: we want to have control over this process ourselves.
|
||||||
Thanks to Docker labels, we can tell Traefik how to create it's internal routing configuration.
|
Thanks to Docker labels, we can tell Træfik how to create it's internal routing configuration.
|
||||||
|
|
||||||
Let's take a look at the labels themselves for the `app` service, which is a HTTP webservice listing on port 9000:
|
Let's take a look at the labels themselves for the `app` service, which is a HTTP webservice listing on port 9000:
|
||||||
|
|
||||||
|
@ -203,31 +207,56 @@ Let's take a look at the labels themselves for the `app` service, which is a HTT
|
||||||
- "traefik.frontend.rule=Host:app.my-awesome-app.org"
|
- "traefik.frontend.rule=Host:app.my-awesome-app.org"
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.port=9000"
|
- "traefik.port=9000"
|
||||||
|
- "traefik.default.protocol=http"
|
||||||
|
- "traefik.admin.frontend.rule=Host:admin-app.my-awesome-app.org"
|
||||||
|
- "traefik.admin.protocol=https"
|
||||||
|
- "traefik.admin.port=9443"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
We use both `container labels` and `service labels`.
|
||||||
|
|
||||||
|
#### Container labels
|
||||||
|
|
||||||
First, we specify the `backend` name which corresponds to the actual service we're routing **to**.
|
First, we specify the `backend` name which corresponds to the actual service we're routing **to**.
|
||||||
|
|
||||||
We also tell Traefik to use the `web` network to route HTTP traffic to this container.
|
We also tell Træfik to use the `web` network to route HTTP traffic to this container.
|
||||||
With the `frontend.rule` label, we tell Traefik that we want to route to this container if the incoming HTTP request contains the `Host` `app.my-awesome-app.org`.
|
With the `traefik.enable` label, we tell Træfik to include this container in it's internal configuration.
|
||||||
Essentially, this is the actual rule used for Layer-7 load balancing.
|
|
||||||
With the `traefik.enable` label, we tell Traefik to include this container in it's internal configuration.
|
|
||||||
|
|
||||||
Finally but not unimportantly, we tell Traefik to route **to** port `9000`, since that is the actual TCP/IP port the container actually listens on.
|
With the `frontend.rule` label, we tell Træfik that we want to route to this container if the incoming HTTP request contains the `Host` `app.my-awesome-app.org`.
|
||||||
|
Essentially, this is the actual rule used for Layer-7 load balancing.
|
||||||
|
|
||||||
|
Finally but not unimportantly, we tell Træfik to route **to** port `9000`, since that is the actual TCP/IP port the container actually listens on.
|
||||||
|
|
||||||
|
### Service labels
|
||||||
|
|
||||||
|
`Service labels` allow managing many routes for the same container.
|
||||||
|
|
||||||
|
When both `container labels` and `service labels` are defined, `container labels` are just used as default values for missing `service labels` but no frontend/backend are going to be defined only with these labels.
|
||||||
|
Obviously, labels `traefik.frontend.rule` and `traefik.port` described above, will only be used to complete information set in `service labels` during the container frontends/bakends creation.
|
||||||
|
|
||||||
|
In the example, two service names are defined : `default` and `admin`.
|
||||||
|
They allow creating two frontends and two backends.
|
||||||
|
|
||||||
|
- `default` has only one `service label` : `traefik.default.protocol`.
|
||||||
|
Træfik will use values set in `traefik.frontend.rule` and `traefik.port` to create the `default` frontend and backend.
|
||||||
|
The frontend listens to incoming HTTP requests which contain the `Host` `app.my-awesome-app.org` and redirect them in `HTTP` to the port `9000` of the backend.
|
||||||
|
- `admin` has all the `services labels` needed to create the `admin` frontend and backend (`traefik.admin.frontend.rule`, `traefik.admin.protocol`, `traefik.admin.port`).
|
||||||
|
Træfik will create a frontend to listen to incoming HTTP requests which contain the `Host` `admin-app.my-awesome-app.org` and redirect them in `HTTPS` to the port `9443` of the backend.
|
||||||
|
|
||||||
#### Gotchas and tips
|
#### Gotchas and tips
|
||||||
|
|
||||||
- Always specify the correct port where the container expects HTTP traffic using `traefik.port` label.
|
- Always specify the correct port where the container expects HTTP traffic using `traefik.port` label.
|
||||||
If a container exposes multiple ports, Traefik may forward traffic to the wrong port.
|
If a container exposes multiple ports, Træfik may forward traffic to the wrong port.
|
||||||
Even if a container only exposes one port, you should always write configuration defensively and explicitly.
|
Even if a container only exposes one port, you should always write configuration defensively and explicitly.
|
||||||
- Should you choose to enable the `exposedbydefault` flag in the `traefik.toml` configuration, be aware that all containers that are placed in the same network as Traefik will automatically be reachable from the outside world, for everyone and everyone to see.
|
- Should you choose to enable the `exposedbydefault` flag in the `traefik.toml` configuration, be aware that all containers that are placed in the same network as Træfik will automatically be reachable from the outside world, for everyone and everyone to see.
|
||||||
Usually, this is a bad idea.
|
Usually, this is a bad idea.
|
||||||
- With the `traefik.frontend.auth.basic` label, it's possible for Traefik to provide a HTTP basic-auth challenge for the endpoints you provide the label for.
|
- With the `traefik.frontend.auth.basic` label, it's possible for Træfik to provide a HTTP basic-auth challenge for the endpoints you provide the label for.
|
||||||
- Traefik has built-in support to automatically export [Prometheus](https://prometheus.io) metrics
|
- Træfik has built-in support to automatically export [Prometheus](https://prometheus.io) metrics
|
||||||
- Traefik supports websockets out of the box. In the example above, the `events`-service could be a NodeJS-based application which allows clients to connect using websocket protocol.
|
- Træfik supports websockets out of the box. In the example above, the `events`-service could be a NodeJS-based application which allows clients to connect using websocket protocol.
|
||||||
Thanks to the fact that HTTPS in our example is enforced, these websockets are automatically secure as well (WSS)
|
Thanks to the fact that HTTPS in our example is enforced, these websockets are automatically secure as well (WSS)
|
||||||
|
|
||||||
### Final thoughts
|
### Final thoughts
|
||||||
|
|
||||||
Using Traefik as a Layer-7 load balancer in combination with both Docker and Let's Encrypt provides you with an extremely flexible, powerful and self-configuring solution for your projects.
|
Using Træfik as a Layer-7 load balancer in combination with both Docker and Let's Encrypt provides you with an extremely flexible, powerful and self-configuring solution for your projects.
|
||||||
|
|
||||||
With Let's Encrypt, your endpoints are automatically secured with production-ready SSL certificates that are renewed automatically as well.
|
With Let's Encrypt, your endpoints are automatically secured with production-ready SSL certificates that are renewed automatically as well.
|
||||||
|
|
|
@ -121,6 +121,7 @@ kind: Service
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
metadata:
|
metadata:
|
||||||
name: traefik-ingress-service
|
name: traefik-ingress-service
|
||||||
|
namespace: kube-system
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
k8s-app: traefik-ingress-lb
|
k8s-app: traefik-ingress-lb
|
||||||
|
@ -185,6 +186,7 @@ kind: Service
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
metadata:
|
metadata:
|
||||||
name: traefik-ingress-service
|
name: traefik-ingress-service
|
||||||
|
namespace: kube-system
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
k8s-app: traefik-ingress-lb
|
k8s-app: traefik-ingress-lb
|
||||||
|
|
|
@ -129,6 +129,11 @@ func (s *DockerSuite) TestDockerContainersWithLabels(c *check.C) {
|
||||||
}
|
}
|
||||||
s.startContainerWithLabels(c, "swarm:1.0.0", labels, "manage", "token://blabla")
|
s.startContainerWithLabels(c, "swarm:1.0.0", labels, "manage", "token://blabla")
|
||||||
|
|
||||||
|
// Start another container by replacing a '.' by a '-'
|
||||||
|
labels = map[string]string{
|
||||||
|
types.LabelFrontendRule: "Host:my-super.host",
|
||||||
|
}
|
||||||
|
s.startContainerWithLabels(c, "swarm:1.0.0", labels, "manage", "token://blablabla")
|
||||||
// Start traefik
|
// Start traefik
|
||||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||||
defer display(c)
|
defer display(c)
|
||||||
|
@ -138,12 +143,20 @@ func (s *DockerSuite) TestDockerContainersWithLabels(c *check.C) {
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/version", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/version", nil)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
req.Host = "my.super.host"
|
req.Host = "my-super.host"
|
||||||
|
|
||||||
// FIXME Need to wait than 500 milliseconds more (for swarm or traefik to boot up ?)
|
// FIXME Need to wait than 500 milliseconds more (for swarm or traefik to boot up ?)
|
||||||
resp, err := try.ResponseUntilStatusCode(req, 1500*time.Millisecond, http.StatusOK)
|
resp, err := try.ResponseUntilStatusCode(req, 1500*time.Millisecond, http.StatusOK)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/version", nil)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
req.Host = "my.super.host"
|
||||||
|
|
||||||
|
// FIXME Need to wait than 500 milliseconds more (for swarm or traefik to boot up ?)
|
||||||
|
resp, err = try.ResponseUntilStatusCode(req, 1500*time.Millisecond, http.StatusOK)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
@ -179,3 +192,40 @@ func (s *DockerSuite) TestDockerContainersWithOneMissingLabels(c *check.C) {
|
||||||
err = try.Request(req, 1500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
err = try.Request(req, 1500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestDockerContainersWithServiceLabels allows cheking the labels behavior
|
||||||
|
// Use service label if defined and compete information with container labels.
|
||||||
|
func (s *DockerSuite) TestDockerContainersWithServiceLabels(c *check.C) {
|
||||||
|
file := s.adaptFileForHost(c, "fixtures/docker/simple.toml")
|
||||||
|
defer os.Remove(file)
|
||||||
|
// Start a container with some labels
|
||||||
|
labels := map[string]string{
|
||||||
|
types.LabelPrefix + "servicename.frontend.rule": "Host:my.super.host",
|
||||||
|
types.LabelFrontendRule: "Host:my.wrong.host",
|
||||||
|
types.LabelPort: "2375",
|
||||||
|
}
|
||||||
|
s.startContainerWithLabels(c, "swarm:1.0.0", labels, "manage", "token://blabla")
|
||||||
|
|
||||||
|
// Start traefik
|
||||||
|
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||||
|
defer display(c)
|
||||||
|
err := cmd.Start()
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/version", nil)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
req.Host = "my.super.host"
|
||||||
|
|
||||||
|
// FIXME Need to wait than 500 milliseconds more (for swarm or traefik to boot up ?)
|
||||||
|
resp, err := try.ResponseUntilStatusCode(req, 1500*time.Millisecond, http.StatusOK)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
var version map[string]interface{}
|
||||||
|
|
||||||
|
c.Assert(json.Unmarshal(body, &version), checker.IsNil)
|
||||||
|
c.Assert(version["Version"], checker.Equals, "swarm/1.0.0")
|
||||||
|
}
|
||||||
|
|
|
@ -29,11 +29,12 @@ import (
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
"github.com/docker/go-connections/sockets"
|
"github.com/docker/go-connections/sockets"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// SwarmAPIVersion is a constant holding the version of the Provider API traefik will use
|
// SwarmAPIVersion is a constant holding the version of the Provider API traefik will use
|
||||||
SwarmAPIVersion string = "1.24"
|
SwarmAPIVersion = "1.24"
|
||||||
// SwarmDefaultWatchTime is the duration of the interval when polling docker
|
// SwarmDefaultWatchTime is the duration of the interval when polling docker
|
||||||
SwarmDefaultWatchTime = 15 * time.Second
|
SwarmDefaultWatchTime = 15 * time.Second
|
||||||
|
|
||||||
|
@ -306,8 +307,8 @@ func (p *Provider) loadDockerConfig(containersInspected []dockerData) *types.Con
|
||||||
frontends := map[string][]dockerData{}
|
frontends := map[string][]dockerData{}
|
||||||
backends := map[string]dockerData{}
|
backends := map[string]dockerData{}
|
||||||
servers := map[string][]dockerData{}
|
servers := map[string][]dockerData{}
|
||||||
for _, container := range filteredContainers {
|
for idx, container := range filteredContainers {
|
||||||
frontendName := p.getFrontendName(container)
|
frontendName := p.getFrontendName(container, idx)
|
||||||
frontends[frontendName] = append(frontends[frontendName], container)
|
frontends[frontendName] = append(frontends[frontendName], container)
|
||||||
backendName := p.getBackend(container)
|
backendName := p.getBackend(container)
|
||||||
backends[backendName] = container
|
backends[backendName] = container
|
||||||
|
@ -522,9 +523,16 @@ func (p *Provider) getMaxConnExtractorFunc(container dockerData) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) containerFilter(container dockerData) bool {
|
func (p *Provider) containerFilter(container dockerData) bool {
|
||||||
_, err := strconv.Atoi(container.Labels[types.LabelPort])
|
var err error
|
||||||
|
portLabel := "traefik.port label"
|
||||||
|
if p.hasServices(container) {
|
||||||
|
portLabel = "traefik.<serviceName>.port or " + portLabel + "s"
|
||||||
|
err = checkServiceLabelPort(container)
|
||||||
|
} else {
|
||||||
|
_, err = strconv.Atoi(container.Labels[types.LabelPort])
|
||||||
|
}
|
||||||
if len(container.NetworkSettings.Ports) == 0 && err != nil {
|
if len(container.NetworkSettings.Ports) == 0 && err != nil {
|
||||||
log.Debugf("Filtering container without port and no traefik.port label %s", container.Name)
|
log.Debugf("Filtering container without port and no %s %s : %s", portLabel, container.Name, err.Error())
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -554,9 +562,46 @@ func (p *Provider) containerFilter(container dockerData) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) getFrontendName(container dockerData) string {
|
// checkServiceLabelPort checks if all service names have a port service label
|
||||||
|
// or if port container label exists for default value
|
||||||
|
func checkServiceLabelPort(container dockerData) error {
|
||||||
|
// If port container label is present, there is a default values for all ports, use it for the check
|
||||||
|
_, err := strconv.Atoi(container.Labels[types.LabelPort])
|
||||||
|
if err != nil {
|
||||||
|
serviceLabelPorts := make(map[string]struct{})
|
||||||
|
serviceLabels := make(map[string]struct{})
|
||||||
|
portRegexp := regexp.MustCompile(`^traefik\.(?P<service_name>.+?)\.port$`)
|
||||||
|
for label := range container.Labels {
|
||||||
|
// Get all port service labels
|
||||||
|
portLabel := portRegexp.FindStringSubmatch(label)
|
||||||
|
if portLabel != nil && len(portLabel) > 0 {
|
||||||
|
serviceLabelPorts[portLabel[0]] = struct{}{}
|
||||||
|
}
|
||||||
|
// Get only one instance of all service names from service labels
|
||||||
|
servicesLabelNames := servicesPropertiesRegexp.FindStringSubmatch(label)
|
||||||
|
if servicesLabelNames != nil && len(servicesLabelNames) > 0 {
|
||||||
|
serviceLabels[strings.Split(servicesLabelNames[0], ".")[1]] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the number of service labels is different than the number of port services label
|
||||||
|
// there is an error
|
||||||
|
if len(serviceLabels) == len(serviceLabelPorts) {
|
||||||
|
for labelPort := range serviceLabelPorts {
|
||||||
|
_, err = strconv.Atoi(container.Labels[labelPort])
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = errors.New("Port service labels missing, please use traefik.port as default value or define all port service labels")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Provider) getFrontendName(container dockerData, idx int) string {
|
||||||
// Replace '.' with '-' in quoted keys because of this issue https://github.com/BurntSushi/toml/issues/78
|
// Replace '.' with '-' in quoted keys because of this issue https://github.com/BurntSushi/toml/issues/78
|
||||||
return provider.Normalize(p.getFrontendRule(container))
|
return provider.Normalize(p.getFrontendRule(container) + "-" + strconv.Itoa(idx))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFrontendRule returns the frontend rule for the specified container, using
|
// GetFrontendRule returns the frontend rule for the specified container, using
|
||||||
|
@ -597,7 +642,7 @@ func (p *Provider) getIPAddress(container dockerData) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if "host" == container.NetworkSettings.NetworkMode {
|
if container.NetworkSettings.NetworkMode.IsHost() {
|
||||||
if container.Node != nil {
|
if container.Node != nil {
|
||||||
if container.Node.IPAddress != "" {
|
if container.Node.IPAddress != "" {
|
||||||
return container.Node.IPAddress
|
return container.Node.IPAddress
|
||||||
|
@ -607,6 +652,21 @@ func (p *Provider) getIPAddress(container dockerData) string {
|
||||||
return "127.0.0.1"
|
return "127.0.0.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if container.NetworkSettings.NetworkMode.IsContainer() {
|
||||||
|
dockerClient, err := p.createClient()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Unable to get IP address for container %s, error: %s", container.Name, err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
ctx := context.Background()
|
||||||
|
containerInspected, err := dockerClient.ContainerInspect(ctx, container.NetworkSettings.NetworkMode.ConnectedContainer())
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Unable to get IP address for container %s : Failed to inspect container ID %s, error: %s", container.Name, container.NetworkSettings.NetworkMode.ConnectedContainer(), err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return p.getIPAddress(parseContainer(containerInspected))
|
||||||
|
}
|
||||||
|
|
||||||
if p.UseBindPortIP {
|
if p.UseBindPortIP {
|
||||||
port := p.getPort(container)
|
port := p.getPort(container)
|
||||||
for netport, portBindings := range container.NetworkSettings.Ports {
|
for netport, portBindings := range container.NetworkSettings.Ports {
|
||||||
|
|
|
@ -20,38 +20,38 @@ func TestDockerGetFrontendName(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
container: containerJSON(name("foo")),
|
container: containerJSON(name("foo")),
|
||||||
expected: "Host-foo-docker-localhost",
|
expected: "Host-foo-docker-localhost-0",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
container: containerJSON(labels(map[string]string{
|
container: containerJSON(labels(map[string]string{
|
||||||
types.LabelFrontendRule: "Headers:User-Agent,bat/0.1.0",
|
types.LabelFrontendRule: "Headers:User-Agent,bat/0.1.0",
|
||||||
})),
|
})),
|
||||||
expected: "Headers-User-Agent-bat-0-1-0",
|
expected: "Headers-User-Agent-bat-0-1-0-0",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
container: containerJSON(labels(map[string]string{
|
container: containerJSON(labels(map[string]string{
|
||||||
"com.docker.compose.project": "foo",
|
"com.docker.compose.project": "foo",
|
||||||
"com.docker.compose.service": "bar",
|
"com.docker.compose.service": "bar",
|
||||||
})),
|
})),
|
||||||
expected: "Host-bar-foo-docker-localhost",
|
expected: "Host-bar-foo-docker-localhost-0",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
container: containerJSON(labels(map[string]string{
|
container: containerJSON(labels(map[string]string{
|
||||||
types.LabelFrontendRule: "Host:foo.bar",
|
types.LabelFrontendRule: "Host:foo.bar",
|
||||||
})),
|
})),
|
||||||
expected: "Host-foo-bar",
|
expected: "Host-foo-bar-0",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
container: containerJSON(labels(map[string]string{
|
container: containerJSON(labels(map[string]string{
|
||||||
types.LabelFrontendRule: "Path:/test",
|
types.LabelFrontendRule: "Path:/test",
|
||||||
})),
|
})),
|
||||||
expected: "Path-test",
|
expected: "Path-test-0",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
container: containerJSON(labels(map[string]string{
|
container: containerJSON(labels(map[string]string{
|
||||||
types.LabelFrontendRule: "PathPrefix:/test2",
|
types.LabelFrontendRule: "PathPrefix:/test2",
|
||||||
})),
|
})),
|
||||||
expected: "PathPrefix-test2",
|
expected: "PathPrefix-test2-0",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ func TestDockerGetFrontendName(t *testing.T) {
|
||||||
provider := &Provider{
|
provider := &Provider{
|
||||||
Domain: "docker.localhost",
|
Domain: "docker.localhost",
|
||||||
}
|
}
|
||||||
actual := provider.getFrontendName(dockerData)
|
actual := provider.getFrontendName(dockerData, 0)
|
||||||
if actual != e.expected {
|
if actual != e.expected {
|
||||||
t.Errorf("expected %q, got %q", e.expected, actual)
|
t.Errorf("expected %q, got %q", e.expected, actual)
|
||||||
}
|
}
|
||||||
|
@ -897,13 +897,13 @@ func TestDockerLoadDockerConfig(t *testing.T) {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedFrontends: map[string]*types.Frontend{
|
expectedFrontends: map[string]*types.Frontend{
|
||||||
"frontend-Host-test-docker-localhost": {
|
"frontend-Host-test-docker-localhost-0": {
|
||||||
Backend: "backend-test",
|
Backend: "backend-test",
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
EntryPoints: []string{},
|
EntryPoints: []string{},
|
||||||
BasicAuth: []string{},
|
BasicAuth: []string{},
|
||||||
Routes: map[string]types.Route{
|
Routes: map[string]types.Route{
|
||||||
"route-frontend-Host-test-docker-localhost": {
|
"route-frontend-Host-test-docker-localhost-0": {
|
||||||
Rule: "Host:test.docker.localhost",
|
Rule: "Host:test.docker.localhost",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -947,24 +947,24 @@ func TestDockerLoadDockerConfig(t *testing.T) {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedFrontends: map[string]*types.Frontend{
|
expectedFrontends: map[string]*types.Frontend{
|
||||||
"frontend-Host-test1-docker-localhost": {
|
"frontend-Host-test1-docker-localhost-0": {
|
||||||
Backend: "backend-foobar",
|
Backend: "backend-foobar",
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
EntryPoints: []string{"http", "https"},
|
EntryPoints: []string{"http", "https"},
|
||||||
BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"},
|
BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"},
|
||||||
Routes: map[string]types.Route{
|
Routes: map[string]types.Route{
|
||||||
"route-frontend-Host-test1-docker-localhost": {
|
"route-frontend-Host-test1-docker-localhost-0": {
|
||||||
Rule: "Host:test1.docker.localhost",
|
Rule: "Host:test1.docker.localhost",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"frontend-Host-test2-docker-localhost": {
|
"frontend-Host-test2-docker-localhost-1": {
|
||||||
Backend: "backend-foobar",
|
Backend: "backend-foobar",
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
EntryPoints: []string{},
|
EntryPoints: []string{},
|
||||||
BasicAuth: []string{},
|
BasicAuth: []string{},
|
||||||
Routes: map[string]types.Route{
|
Routes: map[string]types.Route{
|
||||||
"route-frontend-Host-test2-docker-localhost": {
|
"route-frontend-Host-test2-docker-localhost-1": {
|
||||||
Rule: "Host:test2.docker.localhost",
|
Rule: "Host:test2.docker.localhost",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1005,13 +1005,13 @@ func TestDockerLoadDockerConfig(t *testing.T) {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedFrontends: map[string]*types.Frontend{
|
expectedFrontends: map[string]*types.Frontend{
|
||||||
"frontend-Host-test1-docker-localhost": {
|
"frontend-Host-test1-docker-localhost-0": {
|
||||||
Backend: "backend-foobar",
|
Backend: "backend-foobar",
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
EntryPoints: []string{"http", "https"},
|
EntryPoints: []string{"http", "https"},
|
||||||
BasicAuth: []string{},
|
BasicAuth: []string{},
|
||||||
Routes: map[string]types.Route{
|
Routes: map[string]types.Route{
|
||||||
"route-frontend-Host-test1-docker-localhost": {
|
"route-frontend-Host-test1-docker-localhost-0": {
|
||||||
Rule: "Host:test1.docker.localhost",
|
Rule: "Host:test1.docker.localhost",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1104,3 +1104,53 @@ func TestDockerHasStickinessLabel(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDockerCheckPortLabels(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
container docker.ContainerJSON
|
||||||
|
expectedError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
container: containerJSON(labels(map[string]string{
|
||||||
|
types.LabelPort: "80",
|
||||||
|
})),
|
||||||
|
expectedError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
container: containerJSON(labels(map[string]string{
|
||||||
|
types.LabelPrefix + "servicename.protocol": "http",
|
||||||
|
types.LabelPrefix + "servicename.port": "80",
|
||||||
|
})),
|
||||||
|
expectedError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
container: containerJSON(labels(map[string]string{
|
||||||
|
types.LabelPrefix + "servicename.protocol": "http",
|
||||||
|
types.LabelPort: "80",
|
||||||
|
})),
|
||||||
|
expectedError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
container: containerJSON(labels(map[string]string{
|
||||||
|
types.LabelPrefix + "servicename.protocol": "http",
|
||||||
|
})),
|
||||||
|
expectedError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for containerID, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(strconv.Itoa(containerID), func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
dockerData := parseContainer(test.container)
|
||||||
|
err := checkServiceLabelPort(dockerData)
|
||||||
|
|
||||||
|
if test.expectedError && err == nil {
|
||||||
|
t.Error("expected an error but got nil")
|
||||||
|
} else if !test.expectedError && err != nil {
|
||||||
|
t.Errorf("expected no error, got %q", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -23,28 +23,28 @@ func TestSwarmGetFrontendName(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
service: swarmService(serviceName("foo")),
|
service: swarmService(serviceName("foo")),
|
||||||
expected: "Host-foo-docker-localhost",
|
expected: "Host-foo-docker-localhost-0",
|
||||||
networks: map[string]*docker.NetworkResource{},
|
networks: map[string]*docker.NetworkResource{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
service: swarmService(serviceLabels(map[string]string{
|
service: swarmService(serviceLabels(map[string]string{
|
||||||
types.LabelFrontendRule: "Headers:User-Agent,bat/0.1.0",
|
types.LabelFrontendRule: "Headers:User-Agent,bat/0.1.0",
|
||||||
})),
|
})),
|
||||||
expected: "Headers-User-Agent-bat-0-1-0",
|
expected: "Headers-User-Agent-bat-0-1-0-0",
|
||||||
networks: map[string]*docker.NetworkResource{},
|
networks: map[string]*docker.NetworkResource{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
service: swarmService(serviceLabels(map[string]string{
|
service: swarmService(serviceLabels(map[string]string{
|
||||||
types.LabelFrontendRule: "Host:foo.bar",
|
types.LabelFrontendRule: "Host:foo.bar",
|
||||||
})),
|
})),
|
||||||
expected: "Host-foo-bar",
|
expected: "Host-foo-bar-0",
|
||||||
networks: map[string]*docker.NetworkResource{},
|
networks: map[string]*docker.NetworkResource{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
service: swarmService(serviceLabels(map[string]string{
|
service: swarmService(serviceLabels(map[string]string{
|
||||||
types.LabelFrontendRule: "Path:/test",
|
types.LabelFrontendRule: "Path:/test",
|
||||||
})),
|
})),
|
||||||
expected: "Path-test",
|
expected: "Path-test-0",
|
||||||
networks: map[string]*docker.NetworkResource{},
|
networks: map[string]*docker.NetworkResource{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -54,7 +54,7 @@ func TestSwarmGetFrontendName(t *testing.T) {
|
||||||
types.LabelFrontendRule: "PathPrefix:/test2",
|
types.LabelFrontendRule: "PathPrefix:/test2",
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
expected: "PathPrefix-test2",
|
expected: "PathPrefix-test2-0",
|
||||||
networks: map[string]*docker.NetworkResource{},
|
networks: map[string]*docker.NetworkResource{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ func TestSwarmGetFrontendName(t *testing.T) {
|
||||||
Domain: "docker.localhost",
|
Domain: "docker.localhost",
|
||||||
SwarmMode: true,
|
SwarmMode: true,
|
||||||
}
|
}
|
||||||
actual := provider.getFrontendName(dockerData)
|
actual := provider.getFrontendName(dockerData, 0)
|
||||||
if actual != e.expected {
|
if actual != e.expected {
|
||||||
t.Errorf("expected %q, got %q", e.expected, actual)
|
t.Errorf("expected %q, got %q", e.expected, actual)
|
||||||
}
|
}
|
||||||
|
@ -660,13 +660,13 @@ func TestSwarmLoadDockerConfig(t *testing.T) {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedFrontends: map[string]*types.Frontend{
|
expectedFrontends: map[string]*types.Frontend{
|
||||||
"frontend-Host-test-docker-localhost": {
|
"frontend-Host-test-docker-localhost-0": {
|
||||||
Backend: "backend-test",
|
Backend: "backend-test",
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
EntryPoints: []string{},
|
EntryPoints: []string{},
|
||||||
BasicAuth: []string{},
|
BasicAuth: []string{},
|
||||||
Routes: map[string]types.Route{
|
Routes: map[string]types.Route{
|
||||||
"route-frontend-Host-test-docker-localhost": {
|
"route-frontend-Host-test-docker-localhost-0": {
|
||||||
Rule: "Host:test.docker.localhost",
|
Rule: "Host:test.docker.localhost",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -714,24 +714,24 @@ func TestSwarmLoadDockerConfig(t *testing.T) {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedFrontends: map[string]*types.Frontend{
|
expectedFrontends: map[string]*types.Frontend{
|
||||||
"frontend-Host-test1-docker-localhost": {
|
"frontend-Host-test1-docker-localhost-0": {
|
||||||
Backend: "backend-foobar",
|
Backend: "backend-foobar",
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
EntryPoints: []string{"http", "https"},
|
EntryPoints: []string{"http", "https"},
|
||||||
BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"},
|
BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"},
|
||||||
Routes: map[string]types.Route{
|
Routes: map[string]types.Route{
|
||||||
"route-frontend-Host-test1-docker-localhost": {
|
"route-frontend-Host-test1-docker-localhost-0": {
|
||||||
Rule: "Host:test1.docker.localhost",
|
Rule: "Host:test1.docker.localhost",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"frontend-Host-test2-docker-localhost": {
|
"frontend-Host-test2-docker-localhost-1": {
|
||||||
Backend: "backend-foobar",
|
Backend: "backend-foobar",
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
EntryPoints: []string{},
|
EntryPoints: []string{},
|
||||||
BasicAuth: []string{},
|
BasicAuth: []string{},
|
||||||
Routes: map[string]types.Route{
|
Routes: map[string]types.Route{
|
||||||
"route-frontend-Host-test2-docker-localhost": {
|
"route-frontend-Host-test2-docker-localhost-1": {
|
||||||
Rule: "Host:test2.docker.localhost",
|
Rule: "Host:test2.docker.localhost",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -93,7 +93,9 @@ func TestMarathonLoadConfigNonAPIErrors(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedBackends: nil,
|
expectedBackends: map[string]*types.Backend{
|
||||||
|
"backend-app": {},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "load balancer / circuit breaker labels",
|
desc: "load balancer / circuit breaker labels",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
mkdocs==0.16.3
|
mkdocs==0.16.3
|
||||||
pymdown-extensions>=1.4
|
pymdown-extensions>=1.4
|
||||||
mkdocs-bootswatch>=0.4.0
|
mkdocs-bootswatch>=0.4.0
|
||||||
mkdocs-material>=1.8.1
|
mkdocs-material==1.12.2
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
{{range $app := $apps}}
|
{{range $app := $apps}}
|
||||||
{{range $serviceIndex, $serviceName := getServiceNames $app}}
|
{{range $serviceIndex, $serviceName := getServiceNames $app}}
|
||||||
|
[backends."backend{{getBackend $app $serviceName }}"]
|
||||||
{{ if hasMaxConnLabels $app }}
|
{{ if hasMaxConnLabels $app }}
|
||||||
[backends."backend{{getBackend $app $serviceName }}".maxconn]
|
[backends."backend{{getBackend $app $serviceName }}".maxconn]
|
||||||
amount = {{getMaxConnAmount $app }}
|
amount = {{getMaxConnAmount $app }}
|
||||||
|
|
Loading…
Reference in a new issue