Merge branch 'v1.5' into master

This commit is contained in:
Fernandez Ludovic 2018-02-07 15:24:44 +01:00
commit f5adea1061
15 changed files with 196 additions and 35 deletions

View file

@ -64,7 +64,7 @@ Once your environment is set up and the Træfik repository cloned you can build
cd ~/go/src/github.com/containous/traefik cd ~/go/src/github.com/containous/traefik
# Get go-bindata. Please note, the ellipses are required # Get go-bindata. Please note, the ellipses are required
go get github.com/jteeuwen/go-bindata/... go get github.com/containous/go-bindata/...
# Start build # Start build

2
Gopkg.lock generated
View file

@ -1492,6 +1492,6 @@
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"
analyzer-version = 1 analyzer-version = 1
inputs-digest = "daede4415dbd8614087d38fc4d8cba45d679bbb7185bfca805091ef7b295341f" inputs-digest = "f87576548de74d86b0e93a28551b97317addba5731345338272fdbb8a22ad77f"
solver-name = "gps-cdcl" solver-name = "gps-cdcl"
solver-version = 1 solver-version = 1

View file

@ -295,6 +295,7 @@ func (a *ACME) leadershipListener(elected bool) error {
// CreateLocalConfig creates a tls.config using local ACME configuration // CreateLocalConfig creates a tls.config using local ACME configuration
func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, certs *safe.Safe, checkOnDemandDomain func(domain string) bool) error { func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, certs *safe.Safe, checkOnDemandDomain func(domain string) bool) error {
defer a.runJobs()
err := a.init() err := a.init()
if err != nil { if err != nil {
return err return err
@ -333,7 +334,9 @@ func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, certs *safe.Safe, checkO
a.client, err = a.buildACMEClient(account) a.client, err = a.buildACMEClient(account)
if err != nil { if err != nil {
return err log.Errorf(`Failed to build ACME client: %s
Let's Encrypt functionality will be limited until traefik is restarted.`, err)
return nil
} }
if needRegister { if needRegister {
@ -374,7 +377,6 @@ func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, certs *safe.Safe, checkO
a.retrieveCertificates() a.retrieveCertificates()
a.renewCertificates() a.renewCertificates()
a.runJobs()
ticker := time.NewTicker(24 * time.Hour) ticker := time.NewTicker(24 * time.Hour)
safe.Go(func() { safe.Go(func() {

View file

@ -4,7 +4,7 @@ RUN apk --update upgrade \
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar \ && apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar \
&& rm -rf /var/cache/apk/* && rm -rf /var/cache/apk/*
RUN go get github.com/jteeuwen/go-bindata/... \ RUN go get github.com/containous/go-bindata/... \
&& go get github.com/golang/lint/golint \ && go get github.com/golang/lint/golint \
&& go get github.com/kisielk/errcheck \ && go get github.com/kisielk/errcheck \
&& go get github.com/client9/misspell/cmd/misspell && go get github.com/client9/misspell/cmd/misspell

View file

@ -27,6 +27,7 @@ import (
"github.com/containous/traefik/types" "github.com/containous/traefik/types"
"github.com/containous/traefik/version" "github.com/containous/traefik/version"
"github.com/coreos/go-systemd/daemon" "github.com/coreos/go-systemd/daemon"
"github.com/ogier/pflag"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -75,6 +76,9 @@ Complete documentation is available at https://traefik.io`,
} }
if _, err := f.Parse(usedCmd); err != nil { if _, err := f.Parse(usedCmd); err != nil {
if err == pflag.ErrHelp {
os.Exit(0)
}
fmtlog.Printf("Error parsing command: %s\n", err) fmtlog.Printf("Error parsing command: %s\n", err)
os.Exit(-1) os.Exit(-1)
} }

View file

@ -236,7 +236,7 @@ The following rules are both `Matchers` and `Modifiers`, so the `Matcher` portio
By default, routes will be sorted (in descending order) using rules length (to avoid path overlap): By default, routes will be sorted (in descending order) using rules length (to avoid path overlap):
`PathPrefix:/12345` will be matched before `PathPrefix:/1234` that will be matched before `PathPrefix:/1`. `PathPrefix:/12345` will be matched before `PathPrefix:/1234` that will be matched before `PathPrefix:/1`.
You can customize priority by frontend: You can customize priority by frontend. The priority value is added to the rule length during sorting:
```toml ```toml
[frontends] [frontends]
@ -254,7 +254,7 @@ You can customize priority by frontend:
rule = "PathPrefix:/toto" rule = "PathPrefix:/toto"
``` ```
Here, `frontend1` will be matched before `frontend2` (`10 > 5`). Here, `frontend1` will be matched before `frontend2` (`(3 + 10 == 13) > (4 + 5 == 9)`).
#### Custom headers #### Custom headers

View file

@ -144,6 +144,18 @@ entryPoint = "https"
If `HTTP-01` challenge is used, `acme.httpChallenge.entryPoint` has to be defined and reachable by Let's Encrypt through the port 80. If `HTTP-01` challenge is used, `acme.httpChallenge.entryPoint` has to be defined and reachable by Let's Encrypt through the port 80.
These are Let's Encrypt limitations as described on the [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72). These are Let's Encrypt limitations as described on the [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72).
### Let's Encrypt downtime
Let's Encrypt functionality will be limited until Træfik is restarted.
If Let's Encrypt is not reachable, these certificates will be used :
- ACME certificates already generated before downtime
- Expired ACME certificates
- Provided certificates
!!! note
Default Træfik certificate will be used instead of ACME certificates for new (sub)domains (which need Let's Encrypt challenge).
### `storage` ### `storage`
```toml ```toml

View file

@ -2,6 +2,8 @@
## Reference ## Reference
### TOML
```toml ```toml
[entryPoints] [entryPoints]
[entryPoints.http] [entryPoints.http]
@ -64,6 +66,37 @@
# ... # ...
``` ```
### CLI
For more information about the CLI, see the documentation about [Traefik command](/basics/#traefik).
```shell
--entryPoints='Name:http Address::80'
--entryPoints='Name:https Address::443 TLS'
```
!!! note
Whitespace is used as option separator and `,` is used as value separator for the list.
The names of the options are case-insensitive.
All available options:
```ini
Name:foo
Address::80
TLS:goo,gii
TLS
CA:car
CA.Optional:true
Redirect.EntryPoint:https
Redirect.Regex:http://localhost/(.*)
Redirect.Replacement:http://mydomain/$1
Compress:true
WhiteListSourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16
ProxyProtocol.TrustedIPs:192.168.0.1
ProxyProtocol.Insecure:tue
ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24
```
## Basic ## Basic
@ -118,7 +151,11 @@ To redirect an entrypoint rewriting the URL.
``` ```
!!! note !!! note
Please note that `regex` and `replacement` do not have to be set in the `redirect` structure if an entrypoint is defined for the redirection (they will not be used in this case). Please note that `regex` and `replacement` do not have to be set in the `redirect` structure if an `entrypoint` is defined for the redirection (they will not be used in this case).
Care should be taken when defining replacement expand variables: `$1x` is equivalent to `${1x}`, not `${1}x` (see [Regexp.Expand](https://golang.org/pkg/regexp/#Regexp.Expand)), so use `${1}` syntax.
Regular expressions and replacements can be tested using online tools such as [Go Playground](https://play.golang.org/p/mWU9p-wk2ru) or the [Regex101](https://regex101.com/r/58sIgx/2).
## TLS ## TLS

View file

@ -35,14 +35,14 @@ TL;DR:
```shell ```shell
$ traefik \ $ traefik \
--entrypoints=Name:http Address::80 Redirect.EntryPoint:https \ --entrypoints='Name:http Address::80 Redirect.EntryPoint:https' \
--entrypoints=Name:https Address::443 TLS \ --entrypoints='Name:https Address::443 TLS' \
--defaultentrypoints=http,https --defaultentrypoints=http,https
``` ```
To listen to different ports, we need to create an entry point for each. To listen to different ports, we need to create an entry point for each.
The CLI syntax is `--entrypoints=Name:a_name Address:an_ip_or_empty:a_port options`. The CLI syntax is `--entrypoints='Name:a_name Address:an_ip_or_empty:a_port options'`.
If you want to redirect traffic from one entry point to another, it's the option `Redirect.EntryPoint:entrypoint_name`. If you want to redirect traffic from one entry point to another, it's the option `Redirect.EntryPoint:entrypoint_name`.
By default, we don't want to configure all our services to listen on http and https, we add a default entry point configuration: `--defaultentrypoints=http,https`. By default, we don't want to configure all our services to listen on http and https, we add a default entry point configuration: `--defaultentrypoints=http,https`.
@ -94,8 +94,8 @@ services:
image: traefik:1.5 image: traefik:1.5
command: command:
- "--api" - "--api"
- "--entrypoints=Name:http Address::80 Redirect.EntryPoint:https" - "--entrypoints='Name:http Address::80 Redirect.EntryPoint:https'"
- "--entrypoints=Name:https Address::443 TLS" - "--entrypoints='Name:https Address::443 TLS'"
- "--defaultentrypoints=http,https" - "--defaultentrypoints=http,https"
- "--acme" - "--acme"
- "--acme.storage=/etc/traefik/acme/acme.json" - "--acme.storage=/etc/traefik/acme/acme.json"
@ -204,8 +204,8 @@ services:
command: command:
- "storeconfig" - "storeconfig"
- "--api" - "--api"
- "--entrypoints=Name:http Address::80 Redirect.EntryPoint:https" - "--entrypoints='Name:http Address::80 Redirect.EntryPoint:https'"
- "--entrypoints=Name:https Address::443 TLS" - "--entrypoints='Name:https Address::443 TLS'"
- "--defaultentrypoints=http,https" - "--defaultentrypoints=http,https"
- "--acme" - "--acme"
- "--acme.storage=traefik/acme/account" - "--acme.storage=traefik/acme/account"

View file

@ -110,7 +110,7 @@ entryPoint = "http"
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 messages
- Check for new versions of Træfik 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 Træfik 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!**

View file

@ -1,6 +1,6 @@
# Key-value store configuration # Key-value store configuration
Both [static global configuration](/user-guide/kv-config/#static-configuration-in-key-value-store) and [dynamic](/user-guide/kv-config/#dynamic-configuration-in-key-value-store) configuration can be sorted in a Key-value store. Both [static global configuration](/user-guide/kv-config/#static-configuration-in-key-value-store) and [dynamic](/user-guide/kv-config/#dynamic-configuration-in-key-value-store) configuration can be stored in a Key-value store.
This section explains how to launch Træfik using a configuration loaded from a Key-value store. This section explains how to launch Træfik using a configuration loaded from a Key-value store.

View file

@ -142,6 +142,19 @@ func (s *AcmeSuite) TestOnHostRuleRetrieveAcmeCertificateWithDynamicWildcard(c *
s.retrieveAcmeCertificate(c, testCase) s.retrieveAcmeCertificate(c, testCase)
} }
// Test Let's encrypt down
func (s *AcmeSuite) TestNoValidLetsEncryptServer(c *check.C) {
cmd, display := s.traefikCmd(withConfigFile("fixtures/acme/wrong_acme.toml"))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
// Expected traefik works
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 10*time.Second, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
}
// Doing an HTTPS request and test the response certificate // Doing an HTTPS request and test the response certificate
func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase AcmeTestCase) { func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase AcmeTestCase) {
file := s.adaptFile(c, testCase.traefikConfFilePath, struct { file := s.adaptFile(c, testCase.traefikConfFilePath, struct {

View file

@ -0,0 +1,34 @@
logLevel = "DEBUG"
defaultEntryPoints = ["http", "https"]
[api]
[entryPoints]
[entryPoints.http]
address = ":8081"
[entryPoints.https]
address = ":5001"
[entryPoints.https.tls]
[acme]
email = "test@traefik.io"
storage = "/dev/null"
entryPoint = "https"
OnHostRule = true
caServer = "http://wrongurl:4000/directory"
[file]
[backends]
[backends.backend]
[backends.backend.servers.server1]
url = "http://127.0.0.1:9010"
[frontends]
[frontends.frontend]
backend = "backend"
[frontends.frontend.routes.test]
rule = "Host:traefik.acme.wtf"

View file

@ -352,20 +352,18 @@ func listServices(ctx context.Context, dockerClient client.APIClient) ([]dockerD
for _, service := range serviceList { for _, service := range serviceList {
dData := parseService(service, networkMap) dData := parseService(service, networkMap)
if len(dData.NetworkSettings.Networks) > 0 {
useSwarmLB := isBackendLBSwarm(dData)
if useSwarmLB { if isBackendLBSwarm(dData) {
if len(dData.NetworkSettings.Networks) > 0 {
dockerDataList = append(dockerDataList, dData) dockerDataList = append(dockerDataList, dData)
}
} else {
isGlobalSvc := service.Spec.Mode.Global != nil
dockerDataListTasks, err = listTasks(ctx, dockerClient, service.ID, dData, networkMap, isGlobalSvc)
if err != nil {
log.Warn(err)
} else { } else {
isGlobalSvc := service.Spec.Mode.Global != nil dockerDataList = append(dockerDataList, dockerDataListTasks...)
dockerDataListTasks, err = listTasks(ctx, dockerClient, service.ID, dData, networkMap, isGlobalSvc)
if err != nil {
log.Warn(err)
} else {
dockerDataList = append(dockerDataList, dockerDataListTasks...)
}
} }
} }
} }
@ -381,10 +379,11 @@ func parseService(service swarmtypes.Service, networkMap map[string]*dockertypes
} }
if service.Spec.EndpointSpec != nil { if service.Spec.EndpointSpec != nil {
switch service.Spec.EndpointSpec.Mode { if service.Spec.EndpointSpec.Mode == swarmtypes.ResolutionModeDNSRR {
case swarmtypes.ResolutionModeDNSRR: if isBackendLBSwarm(dData) {
log.Warnf("Ignored endpoint-mode not supported, service name: %s", service.Spec.Annotations.Name) log.Warnf("Ignored %s endpoint-mode not supported, service name: %s. Fallback to Træfik load balancing", swarmtypes.ResolutionModeDNSRR, service.Spec.Annotations.Name)
case swarmtypes.ResolutionModeVIP: }
} else if service.Spec.EndpointSpec.Mode == swarmtypes.ResolutionModeVIP {
dData.NetworkSettings.Networks = make(map[string]*networkData) dData.NetworkSettings.Networks = make(map[string]*networkData)
for _, virtualIP := range service.Endpoint.VirtualIPs { for _, virtualIP := range service.Endpoint.VirtualIPs {
networkService := networkMap[virtualIP.NetworkID] networkService := networkMap[virtualIP.NetworkID]

View file

@ -80,6 +80,7 @@ type fakeServicesClient struct {
dockerVersion string dockerVersion string
networks []dockertypes.NetworkResource networks []dockertypes.NetworkResource
services []swarm.Service services []swarm.Service
tasks []swarm.Task
err error err error
} }
@ -95,10 +96,15 @@ func (c *fakeServicesClient) NetworkList(ctx context.Context, options dockertype
return c.networks, c.err return c.networks, c.err
} }
func (c *fakeServicesClient) TaskList(ctx context.Context, options dockertypes.TaskListOptions) ([]swarm.Task, error) {
return c.tasks, c.err
}
func TestListServices(t *testing.T) { func TestListServices(t *testing.T) {
testCases := []struct { testCases := []struct {
desc string desc string
services []swarm.Service services []swarm.Service
tasks []swarm.Task
dockerVersion string dockerVersion string
networks []dockertypes.NetworkResource networks []dockertypes.NetworkResource
expectedServices []string expectedServices []string
@ -120,7 +126,8 @@ func TestListServices(t *testing.T) {
swarmService( swarmService(
serviceName("service2"), serviceName("service2"),
serviceLabels(map[string]string{ serviceLabels(map[string]string{
labelDockerNetwork: "barnet", labelDockerNetwork: "barnet",
labelBackendLoadBalancerSwarm: "true",
}), }),
withEndpointSpec(modeDNSSR)), withEndpointSpec(modeDNSSR)),
}, },
@ -145,7 +152,8 @@ func TestListServices(t *testing.T) {
swarmService( swarmService(
serviceName("service2"), serviceName("service2"),
serviceLabels(map[string]string{ serviceLabels(map[string]string{
labelDockerNetwork: "barnet", labelDockerNetwork: "barnet",
labelBackendLoadBalancerSwarm: "true",
}), }),
withEndpointSpec(modeDNSSR)), withEndpointSpec(modeDNSSR)),
}, },
@ -174,13 +182,65 @@ func TestListServices(t *testing.T) {
"service1", "service1",
}, },
}, },
{
desc: "Should return service1 and service2",
services: []swarm.Service{
swarmService(
serviceName("service1"),
serviceLabels(map[string]string{
labelDockerNetwork: "barnet",
}),
withEndpointSpec(modeVIP),
withEndpoint(
virtualIP("yk6l57rfwizjzxxzftn4amaot", "10.11.12.13/24"),
virtualIP("2", "10.11.12.99/24"),
)),
swarmService(
serviceName("service2"),
serviceLabels(map[string]string{
labelDockerNetwork: "barnet",
}),
withEndpointSpec(modeDNSSR)),
},
tasks: []swarm.Task{
swarmTask("id1", taskStatus(taskState(swarm.TaskStateRunning))),
swarmTask("id2", taskStatus(taskState(swarm.TaskStateRunning))),
},
dockerVersion: "1.30",
networks: []dockertypes.NetworkResource{
{
Name: "network_name",
ID: "yk6l57rfwizjzxxzftn4amaot",
Created: time.Now(),
Scope: "swarm",
Driver: "overlay",
EnableIPv6: false,
Internal: true,
Ingress: false,
ConfigOnly: false,
Options: map[string]string{
"com.docker.network.driver.overlay.vxlanid_list": "4098",
"com.docker.network.enable_ipv6": "false",
},
Labels: map[string]string{
"com.docker.stack.namespace": "test",
},
},
},
expectedServices: []string{
"service1.0",
"service1.0",
"service2.0",
"service2.0",
},
},
} }
for caseID, test := range testCases { for caseID, test := range testCases {
test := test test := test
t.Run(strconv.Itoa(caseID), func(t *testing.T) { t.Run(strconv.Itoa(caseID), func(t *testing.T) {
t.Parallel() t.Parallel()
dockerClient := &fakeServicesClient{services: test.services, dockerVersion: test.dockerVersion, networks: test.networks} dockerClient := &fakeServicesClient{services: test.services, tasks: test.tasks, dockerVersion: test.dockerVersion, networks: test.networks}
serviceDockerData, err := listServices(context.Background(), dockerClient) serviceDockerData, err := listServices(context.Background(), dockerClient)
assert.NoError(t, err) assert.NoError(t, err)