diff --git a/docs/content/middlewares/addprefix.md b/docs/content/middlewares/addprefix.md index 1124830b5..5f3ce6298 100644 --- a/docs/content/middlewares/addprefix.md +++ b/docs/content/middlewares/addprefix.md @@ -26,6 +26,12 @@ spec: prefix: /foo ``` +```json tab="Marathon" +"labels": { + "traefik.http.middlewares.add-foo.addprefix.prefix": "/foo" +} +``` + ```yaml tab="Rancher" # Prefixing with /foo labels: diff --git a/docs/content/middlewares/basicauth.md b/docs/content/middlewares/basicauth.md index 6c5f39f87..49ec0f323 100644 --- a/docs/content/middlewares/basicauth.md +++ b/docs/content/middlewares/basicauth.md @@ -28,6 +28,12 @@ spec: - test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0 ``` +```json tab="Marathon" +"labels": { + "traefik.http.middlewares.test-auth.basicauth.users": "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0" +} +``` + ```yaml tab="Rancher" # Declaring the user list labels: diff --git a/docs/content/middlewares/buffering.md b/docs/content/middlewares/buffering.md index 2dc3abff2..ce76b6599 100644 --- a/docs/content/middlewares/buffering.md +++ b/docs/content/middlewares/buffering.md @@ -30,6 +30,12 @@ spec: maxRequestBodyBytes: 250000 ``` +```json tab="Marathon" +"labels": { + "traefik.http.middlewares.limit.buffering.maxRequestBodyBytes": "250000" +} +``` + ```yaml tab="Rancher" # Sets the maximum request body to 2Mb labels: diff --git a/docs/content/middlewares/chain.md b/docs/content/middlewares/chain.md index cfb805f26..1178976ef 100644 --- a/docs/content/middlewares/chain.md +++ b/docs/content/middlewares/chain.md @@ -83,6 +83,19 @@ spec: - 127.0.0.1/32 ``` +```json tab="Marathon" +"labels": { + "traefik.http.routers.router1.service": "service1", + "traefik.http.routers.router1.middlewares": "secured", + "traefik.http.routers.router1.rule": "Host(`mydomain`)", + "traefik.http.middlewares.secured.chain.middlewares": "https-only,known-ips,auth-users", + "traefik.http.middlewares.auth-users.basicauth.users": "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", + "traefik.http.middlewares.https-only.schemeredirect.scheme": "https", + "traefik.http.middlewares.known-ips.ipwhitelist.sourceRange": "192.168.1.7,127.0.0.1/32", + "http.services.service1.loadbalancer.server.port": "80" +} +``` + ```yaml tab="Rancher" labels: - "traefik.http.routers.router1.service=service1" diff --git a/docs/content/middlewares/circuitbreaker.md b/docs/content/middlewares/circuitbreaker.md index 05c27f795..1a970c24a 100644 --- a/docs/content/middlewares/circuitbreaker.md +++ b/docs/content/middlewares/circuitbreaker.md @@ -40,6 +40,12 @@ spec: expression: LatencyAtQuantileMS(50.0) > 100 ``` +```json tab="Marathon" +"labels": { + "traefik.http.middlewares.latency-check.circuitbreaker.expression": "LatencyAtQuantileMS(50.0) > 100" +} +``` + ```yaml tab="Rancher" # Latency Check labels: diff --git a/docs/content/middlewares/compress.md b/docs/content/middlewares/compress.md index a097fa8f4..755b50160 100644 --- a/docs/content/middlewares/compress.md +++ b/docs/content/middlewares/compress.md @@ -15,6 +15,12 @@ labels: - "traefik.http.middlewares.test-compress.compress=true" ``` +```json tab="Marathon" +"labels": { + "traefik.http.middlewares.test-compress.compress": "true" +} +``` + ```yaml tab="Rancher" # Enable gzip compression labels: diff --git a/docs/content/middlewares/digestauth.md b/docs/content/middlewares/digestauth.md index 803949786..36fe53ecc 100644 --- a/docs/content/middlewares/digestauth.md +++ b/docs/content/middlewares/digestauth.md @@ -27,6 +27,12 @@ spec: - test2:traefik:518845800f9e2bfb1f1f740ec24f074e ``` +```json tab="Marathon" +"labels": { + "traefik.http.middlewares.test-auth.digestauth.users": "test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e" +} +``` + ```yaml tab="Rancher" labels: - "traefik.http.middlewares.test-auth.digestauth.users=test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e" diff --git a/docs/content/middlewares/errorpages.md b/docs/content/middlewares/errorpages.md index 4174bdeee..6e4b21c1f 100644 --- a/docs/content/middlewares/errorpages.md +++ b/docs/content/middlewares/errorpages.md @@ -33,6 +33,14 @@ spec: query: /{status}.html ``` +```json tab="Marathon" +"labels": { + "traefik.http.middlewares.test-errorpage.errors.status": "500-599", + "traefik.http.middlewares.test-errorpage.errors.service": "serviceError", + "traefik.http.middlewares.test-errorpage.errors.query": "/{status}.html" +} +``` + ```yaml tab="Rancher" # Dynamic Custom Error Page for 5XX Status Code labels: diff --git a/docs/content/middlewares/forwardauth.md b/docs/content/middlewares/forwardauth.md index 613bb15b9..63fc37c44 100644 --- a/docs/content/middlewares/forwardauth.md +++ b/docs/content/middlewares/forwardauth.md @@ -43,6 +43,19 @@ spec: key: path/to/foo.key ``` +```json tab="Marathon" +"labels": { + "traefik.http.middlewares.test-auth.ForwardAuth.Address": "https://authserver.com/auth", + "traefik.http.middlewares.test-auth.ForwardAuth.AuthResponseHeaders": "X-Auth-User,X-Secret", + "traefik.http.middlewares.test-auth.ForwardAuth.TLS.CA": "path/to/local.crt", + "traefik.http.middlewares.test-auth.ForwardAuth.TLS.CAOptional": "true", + "traefik.http.middlewares.test-auth.ForwardAuth.TLS.Cert": "path/to/foo.cert", + "traefik.http.middlewares.test-auth.ForwardAuth.TLS.InsecureSkipVerify": "true", + "traefik.http.middlewares.test-auth.ForwardAuth.TLS.Key": "path/to/foo.key", + "traefik.http.middlewares.test-auth.ForwardAuth.TrustForwardHeader": "true" +} +``` + ```yaml tab="Rancher" # Forward authentication to authserver.com labels: diff --git a/docs/content/middlewares/headers.md b/docs/content/middlewares/headers.md index 7acd5982a..f44d66a3c 100644 --- a/docs/content/middlewares/headers.md +++ b/docs/content/middlewares/headers.md @@ -32,6 +32,13 @@ spec: X-Custom-Response-Header: "True" ``` +```json tab="Marathon" +"labels": { + "traefik.http.middlewares.testHeader.Headers.CustomRequestHeaders.X-Script-Name": "test", + "traefik.http.middlewares.testHeader.Headers.CustomResponseHeaders.X-Custom-Response-Header": "True" +} +``` + ```yaml tab="Rancher" labels: - "traefik.http.middlewares.testHeader.Headers.CustomRequestHeaders.X-Script-Name=test" diff --git a/docs/content/middlewares/ipwhitelist.md b/docs/content/middlewares/ipwhitelist.md index 01d4bf4a1..bf616450b 100644 --- a/docs/content/middlewares/ipwhitelist.md +++ b/docs/content/middlewares/ipwhitelist.md @@ -27,6 +27,12 @@ spec: - 192.168.1.7 ``` +```json tab="Marathon" +"labels": { + "traefik.http.middlewares.test-ipwhitelist.IPWhiteList.SourceRange": "127.0.0.1/32,192.168.1.7" +} +``` + ```yaml tab="Rancher" # Accepts request from defined IP labels: diff --git a/docs/content/middlewares/maxconnection.md b/docs/content/middlewares/maxconnection.md index 86e3e0459..f39da10a0 100644 --- a/docs/content/middlewares/maxconnection.md +++ b/docs/content/middlewares/maxconnection.md @@ -25,6 +25,12 @@ spec: prefix: /bar ``` +```json tab="Marathon" +"labels": { + "traefik.http.middlewares.test-maxconn.maxconn.amount": "10" +} +``` + ```yaml tab="Rancher" # Limiting to 10 simultaneous connections labels: diff --git a/docs/content/middlewares/overview.md b/docs/content/middlewares/overview.md index a1c751fc0..6219fc3b0 100644 --- a/docs/content/middlewares/overview.md +++ b/docs/content/middlewares/overview.md @@ -59,6 +59,18 @@ spec: - name: stripprefix ``` +```json tab="Marathon" +"labels": { + "traefik.http.middlewares.foo-add-prefix.addprefix.prefix": "/foo" +} +``` + +```yaml tab="Rancher" +# As a Rancher Label +labels: + - "traefik.http.middlewares.foo-add-prefix.addprefix.prefix=/foo" +``` + ```toml tab="File" # As Toml Configuration File [providers] diff --git a/docs/content/middlewares/passtlsclientcert.md b/docs/content/middlewares/passtlsclientcert.md index 6be54cd0d..9fc629903 100644 --- a/docs/content/middlewares/passtlsclientcert.md +++ b/docs/content/middlewares/passtlsclientcert.md @@ -27,6 +27,12 @@ spec: pem: true ``` +```json tab="Marathon" +"labels": { + "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.pem": "true" +} +``` + ```yaml tab="Rancher" # Pass the escaped pem in the `X-Forwarded-Tls-Client-Cert` header. labels: diff --git a/docs/content/middlewares/ratelimit.md b/docs/content/middlewares/ratelimit.md index 33e2ee44f..6c8a747a9 100644 --- a/docs/content/middlewares/ratelimit.md +++ b/docs/content/middlewares/ratelimit.md @@ -43,6 +43,18 @@ spec: burst = 10 ``` +```json tab="Marathon" +"labels": { + "traefik.http.middlewares.test-ratelimit.ratelimit.extractorfunc": "client.ip", + "traefik.http.middlewares.test-ratelimit.ratelimit.rateset.rate0.period": "10s", + "traefik.http.middlewares.test-ratelimit.ratelimit.rateset.rate0.average": "100", + "traefik.http.middlewares.test-ratelimit.ratelimit.rateset.rate0.burst": "200", + "traefik.http.middlewares.test-ratelimit.ratelimit.rateset.rate1.period": "3s", + "traefik.http.middlewares.test-ratelimit.ratelimit.rateset.rate1.average": "5", + "traefik.http.middlewares.test-ratelimit.ratelimit.rateset.rate1.burst": "10" +} +``` + ```yaml tab="Rancher" # Here, an average of 5 requests every 3 seconds is allowed and an average of 100 requests every 10 seconds. # These can "burst" up to 10 and 200 in each period, respectively. diff --git a/docs/content/middlewares/redirectregex.md b/docs/content/middlewares/redirectregex.md index a59f50a89..5fcd90652 100644 --- a/docs/content/middlewares/redirectregex.md +++ b/docs/content/middlewares/redirectregex.md @@ -28,6 +28,13 @@ spec: replacement: http://mydomain/$1 ``` +```json tab="Marathon" +"labels": { + "traefik.http.middlewares.test-redirectregex.redirectregex.regex": "^http://localhost/(.*)", + "traefik.http.middlewares.test-redirectregex.redirectregex.replacement": "http://mydomain/$1" +} +``` + ```yaml tab="Rancher" # Redirect with domain replacement labels: diff --git a/docs/content/middlewares/redirectscheme.md b/docs/content/middlewares/redirectscheme.md index 1515a5d1f..acf3f4279 100644 --- a/docs/content/middlewares/redirectscheme.md +++ b/docs/content/middlewares/redirectscheme.md @@ -26,6 +26,12 @@ spec: scheme: https ``` +```json tab="Marathon" +"labels": { + "traefik.http.middlewares.test-redirectscheme.redirectscheme.scheme": "https" +} +``` + ```yaml tab="Rancher" # Redirect to https labels: diff --git a/docs/content/middlewares/replacepath.md b/docs/content/middlewares/replacepath.md index d5522dd50..6270b8f9c 100644 --- a/docs/content/middlewares/replacepath.md +++ b/docs/content/middlewares/replacepath.md @@ -26,6 +26,12 @@ spec: path: /foo ``` +```json tab="Marathon" +"labels": { + "traefik.http.middlewares.test-replacepath.replacepath.path": "/foo" +} +``` + ```yaml tab="Rancher" # Replace the path by /foo labels: diff --git a/docs/content/middlewares/replacepathregex.md b/docs/content/middlewares/replacepathregex.md index 4c70be132..2b977c3bb 100644 --- a/docs/content/middlewares/replacepathregex.md +++ b/docs/content/middlewares/replacepathregex.md @@ -28,6 +28,13 @@ spec: replacement: /bar/$1 ``` +```json tab="Marathon" +"labels": { + "traefik.http.middlewares.test-replacepathregex.replacepathregex.regex": "^/foo/(.*)", + "traefik.http.middlewares.test-replacepathregex.replacepathregex.replacement": "/bar/$1" +} +``` + ```yaml tab="Rancher" # Replace path with regex labels: diff --git a/docs/content/middlewares/retry.md b/docs/content/middlewares/retry.md index 1227eb0da..7cdd590b2 100644 --- a/docs/content/middlewares/retry.md +++ b/docs/content/middlewares/retry.md @@ -26,6 +26,12 @@ spec: attempts: 4 ``` +```json tab="Marathon" +"labels": { + "traefik.http.middlewares.test-retry.retry.attempts": "4" +} +``` + ```yaml tab="Rancher" # Retry to send request 4 times labels: diff --git a/docs/content/middlewares/stripprefix.md b/docs/content/middlewares/stripprefix.md index 151f8d535..3f9f25c16 100644 --- a/docs/content/middlewares/stripprefix.md +++ b/docs/content/middlewares/stripprefix.md @@ -26,6 +26,12 @@ spec: prefixes: "foobar, fiibar" ``` +```json tab="Marathon" +"labels": { + "traefik.http.middlewares.test-stripprefix.stripprefix.prefixes": "foobar, fiibar" +} +``` + ```yaml tab="Rancher" # Replace the path by /foo labels: diff --git a/docs/content/middlewares/stripprefixregex.md b/docs/content/middlewares/stripprefixregex.md index e1f38db2f..8691cb24c 100644 --- a/docs/content/middlewares/stripprefixregex.md +++ b/docs/content/middlewares/stripprefixregex.md @@ -26,6 +26,12 @@ spec: regex: "^/foo/(.*)" ``` +```json tab="Marathon" +"labels": { + "traefik.http.middlewares.test-stripprefixregex.stripprefixregex.regex": "^/foo/(.*)" +} +``` + ```yaml tab="Rancher" # Replace the path by /foo labels: diff --git a/docs/content/providers/docker.md b/docs/content/providers/docker.md index 218ef553c..856ffe8e3 100644 --- a/docs/content/providers/docker.md +++ b/docs/content/providers/docker.md @@ -180,14 +180,17 @@ Defines a default docker network to use for connections to all containers. This option can be overridden on a container basis with the `traefik.docker.network` label. -### `domain` +### `defaultRule` -_Optional_ +_Optional, Default=Host(`{{ normalize .Name }}`)_ -This is the default base domain used for the router rules. +For a given container if no routing rule was defined by a label, it is defined by this defaultRule instead. +It must be a valid [Go template](https://golang.org/pkg/text/template/), +augmented with the [sprig template functions](http://masterminds.github.io/sprig/). +The container service name can be accessed as the Name identifier, +and the template has access to all the labels defined on this container. -This option can be overridden on a container basis with the -`traefik.domain` label. +``defaultRule = "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"`` ### `swarmMode` @@ -207,7 +210,8 @@ Defines the polling interval (in seconds) in Swarm Mode. Traefik creates, for each container, a corresponding [service](../routing/services/index.md) and [router](../routing/routers/index.md). -The Service automatically gets a server per instance of the container, and the router gets a default rule attached to it, based on the container name. +The Service automatically gets a server per instance of the container, +and the router automatically gets a rule defined by defaultRule (if no rule for it was defined in labels). ### Routers @@ -223,7 +227,7 @@ Every [Service](../routing/services/index.md) parameter can be updated this way. ### Middleware -You can declare pieces of middleware using labels starting with `traefik.http.middlewares.{name-of-your-choice}.`, followed by the middleware type/options. For example, to declare a middleware [`schemeredirect`](../middlewares/redirectscheme.md) named `my-redirect`, you'd write `traefik.http.middlewares.my-redirect.schemeredirect.scheme: https`. +You can declare pieces of middleware using labels starting with `traefik.http.middlewares.{name-of-your-choice}.`, followed by the middleware type/options. For example, to declare a middleware [`redirectscheme`](../middlewares/redirectscheme.md) named `my-redirect`, you'd write `traefik.http.middlewares.my-redirect.redirectscheme.scheme: https`. ??? example "Declaring and Referencing a Middleware" @@ -281,3 +285,10 @@ If a container is linked to several networks, be sure to set the proper network !!! warning When deploying a stack from a compose file `stack`, the networks defined are prefixed with `stack`. + +#### `traefik.docker.lbswarm` + +Enables Swarm's inbuilt load balancer (only relevant in Swarm Mode). + +If you enable this option, Traefik will use the virtual IP provided by docker swarm instead of the containers IPs. +Which means that Traefik will not perform any kind of load balancing and will delegate this task to swarm. diff --git a/docs/content/providers/marathon.md b/docs/content/providers/marathon.md new file mode 100644 index 000000000..589df9244 --- /dev/null +++ b/docs/content/providers/marathon.md @@ -0,0 +1,267 @@ +# Traefik & Marathon + +Traefik can be configured to use Marathon as a provider. +{: .subtitle } + +See also [Marathon user guide](../user-guides/marathon.md). + +## Configuration Examples + +??? example "Configuring Marathon & Deploying / Exposing Applications" + + Enabling the marathon provider + + ```toml + [providers.marathon] + endpoint = "http://127.0.0.1:8080" + ``` + + Attaching labels to marathon applications + + ```json + { + "id": "/whoami", + "container": { + "type": "DOCKER", + "docker": { + "image": "containous/whoami", + "network": "BRIDGE", + "portMappings": [ + { + "containerPort": 80, + "hostPort": 0, + "protocol": "tcp" + } + ] + } + }, + "labels": { + "traefik.http.Routers.app.Rule": "PathPrefix(`/app`)" + } + } + ``` + +## Provider Configuration Options + +!!! tip "Browse the Reference" + If you're in a hurry, maybe you'd rather go through the [static](../reference/static-configuration.md) and the [dynamic](../reference/dynamic-configuration/marathon.md) configuration references. + +### `basic` + +_Optional_ + +Enables Marathon basic authentication. + + ```toml + [marathon.basic] + httpBasicAuthUser = "foo" + httpBasicPassword = "bar" + ``` + +### `dcosToken` + +_Optional_ + +DCOSToken for DCOS environment. +If set, it overrides the Authorization header. + +`dcosToken = "xxxxxx"` + +### `defaultRule` + +_Optional, Default=Host(`{{ normalize .Name }}`)_ + +For a given application if no routing rule was defined by a label, it is defined by this defaultRule instead. +It must be a valid [Go template](https://golang.org/pkg/text/template/), +augmented with the [sprig template functions](http://masterminds.github.io/sprig/). +The app ID can be accessed as the Name identifier, +and the template has access to all the labels defined on this Marathon application. + +``defaultRule = "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"`` + +### `dialerTimeout` + +_Optional, Default=5s_ + +Overrides DialerTimeout. +Amount of time the Marathon provider should wait before timing out, +when trying to open a TCP connection to a Marathon master. +Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration), +or directly as a number of seconds. + +### `endpoint` + +_Optional, Default=http://127.0.0.1:8080_ + +Marathon server endpoint. +You can optionally specify multiple endpoints: + +`endpoint = "http://10.241.1.71:8080,10.241.1.72:8080,10.241.1.73:8080"` + +### `exposedByDefault` + +_Optional, Default=true_ + +Exposes Marathon applications by default through Traefik. +If set to false, applications that don't have a `traefik.enable=true` label will be ignored from the resulting routing configuration. + +### `filterMarathonConstraints` + +_Optional, Default=false_ + +Enables filtering using Marathon constraints. +If enabled, Traefik will take into account Marathon constraints, as defined in https://mesosphere.github.io/marathon/docs/constraints.html +Each individual constraint will be treated as a verbatim compounded tag, +e.g. "rack_id:CLUSTER:rack-1", with all constraint groups concatenated together using ":". + +### `forceTaskHostname` + +_Optional, Default=false_ + +By default, a task's IP address (as returned by the Marathon API) is used as +backend server if an IP-per-task configuration can be found; otherwise, the +name of the host running the task is used. +The latter behavior can be enforced by enabling this switch. + +### `keepAlive` + +_Optional, Default=10s_ + +Set the TCP Keep Alive interval for the Marathon HTTP Client. +Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration), +or directly as a number of seconds. + +### `respectReadinessChecks` + +_Optional, Default=false_ + +Applications may define readiness checks which are probed by Marathon during +deployments periodically, and these check results are exposed via the API. +Enabling respectReadinessChecks causes Traefik to filter out tasks +whose readiness checks have not succeeded. +Note that the checks are only valid at deployment times. +See the Marathon guide for details. + +### `responseHeaderTimeout` + +_Optional, Default=60s_ + +Overrides ResponseHeaderTimeout. +Amount of time the Marathon provider should wait before timing out, +when waiting for the first response header from a Marathon master. +Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration), +or directly as a number of seconds. + +### `TLS` + +_Optional_ + +TLS client configuration. https://golang.org/pkg/crypto/tls/#Config + + ```toml + [marathon.TLS] + CA = "/etc/ssl/ca.crt" + Cert = "/etc/ssl/marathon.cert" + Key = "/etc/ssl/marathon.key" + insecureSkipVerify = true + ``` + +### `TLSHandshakeTimeout` + +_Optional, Default=5s_ + +Overrides TLSHandshakeTimeout. +Amount of time the Marathon provider should wait before timing out, +when waiting for the TLS handkshake to complete. +Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration), +or directly as a number of seconds. + +### `trace` + +_Optional, Default=false_ + +Displays additional provider logs (if available). + +### `watch` + +_Optional, Default=true_ + +Enables watching for Marathon changes. + +## Routing Configuration Options + +### General + +Traefik creates, for each Marathon application, a corresponding [service](../routing/services/index.md) and [router](../routing/routers/index.md). + +The Service automatically gets a server per instance of the application, +and the router automatically gets a rule defined by defaultRule (if no rule for it was defined in labels). + +### Routers + +To update the configuration of the Router automatically attached to the application, add labels starting with `traefik.HTTP.Routers.{router-name-of-your-choice}.` and followed by the option you want to change. For example, to change the routing rule, you could add the label `traefik.HTTP.Routers.Routername.Rule=Host(my-domain)`. + +Every [Router](../routing/routers/index.md) parameter can be updated this way. + +### Services + +To update the configuration of the Service automatically attached to the container, add labels starting with `traefik.HTTP.Services.{service-name-of-your-choice}.`, followed by the option you want to change. For example, to change the load balancer method, you'd add the label `traefik.HTTP.Services.Servicename.LoadBalancer.Method=drr`. + +Every [Service](../routing/services/index.md) parameter can be updated this way. + +### Middleware + +You can declare pieces of middleware using labels starting with `traefik.HTTP.Middlewares.{middleware-name-of-your-choice}.`, followed by the middleware type/options. For example, to declare a middleware [`schemeredirect`](../middlewares/redirectscheme.md) named `my-redirect`, you'd write `traefik.HTTP.Middlewares.my-redirect.RedirectScheme.Scheme: https`. + +??? example "Declaring and Referencing a Middleware" + + ```json + { + ... + "labels": { + "traefik.http.middlewares.my-redirect.schemeredirect.scheme": "https", + "traefik.http.routers.middlewares": "my-redirect" + } + } + ``` + +!!! warning "Conflicts in Declaration" + + If you declare multiple middleware with the same name but with different parameters, the middleware fails to be declared. + +### TCP + +You can declare TCP Routers and/or Services using labels. + +??? example "Declaring TCP Routers and Services" + + ```json + { + ... + "labels": { + "traefik.tcp.routers.my-router.rule": "HostSNI(`my-host.com`)", + "traefik.tcp.routers.my-router.tls": "true", + "traefik.tcp.services.my-service.loadbalancer.server.port": "4123" + } + } + ``` + +!!! warning "TCP and HTTP" + + If you declare a TCP Router/Service, it will prevent Traefik from automatically creating an HTTP Router/Service (as it would by default if no TCP Router/Service is defined). + Both a TCP Router/Service and an HTTP Router/Service can be created for the same application, but it has to be done explicitly in the config. + +### Specific Options + +#### `traefik.enable` + +Setting this option controls whether Traefik exposes the application. +It overrides the value of `exposedByDefault`. + +#### `traefik.tags` + +Sets the tags for [constraints filtering](./overview.md#constraints-configuration). + +#### `traefik.marathon.ipadressidx` + +If a task has several IP addresses, this option specifies which one, in the list of available addresses, to select. diff --git a/docs/content/providers/overview.md b/docs/content/providers/overview.md index 305f31565..65fefb78f 100644 --- a/docs/content/providers/overview.md +++ b/docs/content/providers/overview.md @@ -31,11 +31,15 @@ Below is the list of the currently supported providers in Traefik. | [Docker](./docker.md) | Orchestrator | Label | | [File](./file.md) | Orchestrator | Custom Annotation | | [Kubernetes](kubernetes-crd.md) | Orchestrator | Custom Resource | -| Marathon (not yet documented) | Orchestrator | Label | +| [Marathon](marathon.md) | Orchestrator | Label | !!! note "More Providers" The current version of Traefik is in development and doesn't support (yet) every provider. See the previous version (1.7) for more providers. + + ## Constraints Configuration diff --git a/docs/content/reference/dynamic-configuration/docker.md b/docs/content/reference/dynamic-configuration/docker.md index 720bc24e8..1ba5becff 100644 --- a/docs/content/reference/dynamic-configuration/docker.md +++ b/docs/content/reference/dynamic-configuration/docker.md @@ -4,5 +4,5 @@ Dynamic configuration with Docker Labels {: .subtitle } ```yaml ---8<-- "content/reference/dynamic-configuration/docker.yml" +--8<-- "content/reference/dynamic-configuration/labels.yml" ``` diff --git a/docs/content/reference/dynamic-configuration/docker.yml b/docs/content/reference/dynamic-configuration/labels.yml similarity index 93% rename from docs/content/reference/dynamic-configuration/docker.yml rename to docs/content/reference/dynamic-configuration/labels.yml index 3a8a8592f..30c1c3a18 100644 --- a/docs/content/reference/dynamic-configuration/docker.yml +++ b/docs/content/reference/dynamic-configuration/labels.yml @@ -143,3 +143,17 @@ labels: - "traefik.HTTP.Services.Service1.LoadBalancer.server.Scheme=foobar" - "traefik.HTTP.Services.Service0.LoadBalancer.HealthCheck.Headers.name0=foobar" - "traefik.HTTP.Services.Service1.LoadBalancer.server.Weight=42" +- "traefik.TCP.Routers.Router0.Rule=foobar" +- "traefik.TCP.Routers.Router0.EntryPoints=foobar, fiibar" +- "traefik.TCP.Routers.Router0.Service=foobar" +- "traefik.TCP.Routers.Router0.TLS.Passthrough=false" +- "traefik.TCP.Routers.Router1.Rule=foobar" +- "traefik.TCP.Routers.Router1.EntryPoints=foobar, fiibar" +- "traefik.TCP.Routers.Router1.Service=foobar" +- "traefik.TCP.Routers.Router1.TLS.Passthrough=false" +- "traefik.TCP.Services.Service0.LoadBalancer.Method=foobar" +- "traefik.TCP.Services.Service0.LoadBalancer.server.Port=42" +- "traefik.TCP.Services.Service0.LoadBalancer.server.Weight=42" +- "traefik.TCP.Services.Service1.LoadBalancer.Method=foobar" +- "traefik.TCP.Services.Service1.LoadBalancer.server.Port=42" +- "traefik.TCP.Services.Service1.LoadBalancer.server.Weight=42" diff --git a/docs/content/reference/dynamic-configuration/marathon.md b/docs/content/reference/dynamic-configuration/marathon.md new file mode 100644 index 000000000..60260605d --- /dev/null +++ b/docs/content/reference/dynamic-configuration/marathon.md @@ -0,0 +1,8 @@ +# Marathon Configuration Reference + +Dynamic configuration with Marathon Labels +{: .subtitle } + +```yaml +--8<-- "content/reference/dynamic-configuration/labels.yml" +``` diff --git a/docs/content/user-guides/marathon.md b/docs/content/user-guides/marathon.md new file mode 100644 index 000000000..e1dc501ba --- /dev/null +++ b/docs/content/user-guides/marathon.md @@ -0,0 +1,120 @@ +# Marathon + +This guide explains how to integrate Marathon and operate the cluster in a reliable way from Traefik's standpoint. + +## Host detection + +Marathon offers multiple ways to run (Docker-containerized) applications, the most popular ones being + +- BRIDGE-networked containers with dynamic high ports exposed +- HOST-networked containers with host machine ports +- containers with dedicated IP addresses ([IP-per-task](https://mesosphere.github.io/marathon/docs/ip-per-task.html)). + +Traefik tries to detect the configured mode and route traffic to the right IP addresses. It is possible to force using task hosts with the `forceTaskHostname` option. + +## Port detection + +Traefik also attempts to determine the right port (which is a [non-trivial matter in Marathon](https://mesosphere.github.io/marathon/docs/ports.html)). +Following is the order by which Traefik tries to identify the port (the first one that yields a positive result will be used): + +1. A arbitrary port specified through the `traefik.HTTP.Services.ServiceName.LoadBalancer.server.Port=8080` +1. The task port (possibly indexed through the `traefik.HTTP.Services.ServiceName.LoadBalancer.server.Port=index:0` label, otherwise the first one). +1. The port from the application's `portDefinitions` field (possibly indexed through the `traefik.HTTP.Services.ServiceName.LoadBalancer.server.Port=index:0` label, otherwise the first one). +1. The port from the application's `ipAddressPerTask` field (possibly indexed through the `traefik.HTTP.Services.ServiceName.LoadBalancer.server.Port=index:0` label, otherwise the first one). + +## Achieving high availability + +### Scenarios + +There are three scenarios where the availability of a Marathon application could be impaired along with the risk of losing or failing requests: + +- During the startup phase when Traefik already routes requests to the backend even though it has not completed its bootstrapping process yet. +- During the shutdown phase when Traefik still routes requests to the backend while the backend is already terminating. +- During a failure of the application when Traefik has not yet identified the backend as being erroneous. + +The first two scenarios are common with every rolling upgrade of an application (i.e. a new version release or configuration update). + +The following sub-sections describe how to resolve or mitigate each scenario. + +#### Startup + +It is possible to define [readiness checks](https://mesosphere.github.io/marathon/docs/readiness-checks.html) (available since Marathon version 1.1) per application and have Marathon take these into account during the startup phase. + +The idea is that each application provides an HTTP endpoint that Marathon queries periodically during an ongoing deployment in order to mark the associated readiness check result as successful if and only if the endpoint returns a response within the configured HTTP code range. +As long as the check keeps failing, Marathon will not proceed with the deployment (within the configured upgrade strategy bounds). + +Beginning with version 1.4, Traefik respects readiness check results if the Traefik option is set and checks are configured on the applications accordingly. + +!!! note + Due to the way readiness check results are currently exposed by the Marathon API, ready tasks may be taken into rotation with a small delay. + It is on the order of one readiness check timeout interval (as configured on the application specifiation) and guarantees that non-ready tasks do not receive traffic prematurely. + +If readiness checks are not possible, a current mitigation strategy is to enable [retries](../middlewares/retry.md) and make sure that a sufficient number of healthy application tasks exist so that one retry will likely hit one of those. +Apart from its probabilistic nature, the workaround comes at the price of increased latency. + +#### Shutdown + +It is possible to install a [termination handler](https://mesosphere.github.io/marathon/docs/health-checks.html) (available since Marathon version 1.3) with each application whose responsibility it is to delay the shutdown process long enough until the backend has been taken out of load-balancing rotation with reasonable confidence (i.e., Traefik has received an update from the Marathon event bus, recomputes the available Marathon backends, and applies the new configuration). +Specifically, each termination handler should install a signal handler listening for a SIGTERM signal and implement the following steps on signal reception: + +1. Disable Keep-Alive HTTP connections. +1. Keep accepting HTTP requests for a certain period of time. +1. Stop accepting new connections. +1. Finish serving any in-flight requests. +1. Shut down. + +Traefik already ignores Marathon tasks whose state does not match `TASK_RUNNING`; since terminating tasks transition into the `TASK_KILLING` and eventually `TASK_KILLED` state, there is nothing further that needs to be done on Traefik's end. + +How long HTTP requests should continue to be accepted in step 2 depends on how long Traefik needs to receive and process the Marathon configuration update. +Under regular operational conditions, it should be on the order of seconds, with 10 seconds possibly being a good default value. + +Again, configuring Traefik to do retries (as discussed in the previous section) can serve as a decent workaround strategy. +Paired with termination handlers, they would cover for those cases where either the termination sequence or Traefik cannot complete their part of the orchestration process in time. + +#### Failure + +A failing application always happens unexpectedly, and hence, it is very difficult or even impossible to rule out the adversal effects categorically. + +Failure reasons vary broadly and could stretch from unacceptable slowness, a task crash, or a network split. + +There are two mitigaton efforts: + +1. Configure [Marathon health checks](https://mesosphere.github.io/marathon/docs/health-checks.html) on each application. +2. Configure Traefik health checks (possibly via the `traefik.HTTP.Services.YourServiceName.LoadBalancer.HealthCheck.*` labels) and make sure they probe with proper frequency. + +The Marathon health check makes sure that applications once deemed dysfunctional are being rescheduled to different slaves. +However, they might take a while to get triggered and the follow-up processes to complete. + +For that reason, the Treafik health check provides an additional check that responds more rapidly and does not require a configuration reload to happen. +Additionally, it protects from cases that the Marathon health check may not be able to cover, such as a network split. + +### (Non-)Alternatives + +There are a few alternatives of varying quality that are frequently asked for. + +The remaining section is going to explore them along with a benefit/cost trade-off. + +#### Reusing Marathon health checks + +It may seem obvious to reuse the Marathon health checks as a signal to Traefik whether an application should be taken into load-balancing rotation or not. + +Apart from the increased latency a failing health check may have, a major problem with this is is that Marathon does not persist the health check results. +Consequently, if a master re-election occurs in the Marathon clusters, all health check results will revert to the _unknown_ state, effectively causing all applications inside the cluster to become unavailable and leading to a complete cluster failure. +Re-elections do not only happen during regular maintenance work (often requiring rolling upgrades of the Marathon nodes) but also when the Marathon leader fails spontaneously. +As such, there is no way to handle this situation deterministically. + +Finally, Marathon health checks are not mandatory (the default is to use the task state as reported by Mesos), so requiring them for Traefik would raise the entry barrier for Marathon users. + +Traefik used to use the health check results as a strict requirement but moved away from it as [users reported the dramatic consequences](https://github.com/containous/traefik/issues/653). + +#### Draining + +Another common approach is to let a proxy drain backends that are supposed to shut down. +That is, once a backend is supposed to shut down, Traefik would stop forwarding requests. + +On the plus side, this would not require any modifications to the application in question. +However, implementing this fully within Traefik seems like a non-trivial undertaking. + +Additionally, the approach is less flexible compared to a custom termination handler since only the latter allows for the implementation of custom termination sequences that go beyond simple request draining (e.g., persisting a snapshot state to disk prior to terminating). + +The feature is currently not implemented; a request for draining in general is at [issue 41](https://github.com/containous/traefik/issues/41). diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index b1501457d..7eb33dba7 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -78,6 +78,7 @@ nav: # - 'Kubernetes Ingress': 'providers/kubernetes-ingress.md' - 'Rancher': 'providers/rancher.md' - 'File': 'providers/file.md' + - 'Marathon': 'providers/marathon.md' - 'Routing & Load Balancing': - 'Overview': 'routing/overview.md' - 'Entrypoints': 'routing/entrypoints.md' @@ -120,6 +121,7 @@ nav: - 'Tracing': 'observability/tracing.md' - 'User Guides': - 'Kubernetes and Let''s Encrypt': 'user-guides/crd-acme/index.md' + - 'Marathon': 'user-guides/marathon.md' - 'Contributing': - 'Thank You!': 'contributing/thank-you.md' - 'Submitting Issues': 'contributing/submitting-issues.md' diff --git a/pkg/provider/label/parser_test.go b/pkg/provider/label/parser_test.go index f781a6daf..60af8b4b1 100644 --- a/pkg/provider/label/parser_test.go +++ b/pkg/provider/label/parser_test.go @@ -160,361 +160,477 @@ func TestDecodeConfiguration(t *testing.T) { "traefik.http.services.Service1.loadbalancer.server.port": "8080", "traefik.http.services.Service1.loadbalancer.stickiness": "false", "traefik.http.services.Service1.loadbalancer.stickiness.cookiename": "fui", + "traefik.tcp.routers.Router0.rule": "foobar", + "traefik.tcp.routers.Router0.entrypoints": "foobar, fiibar", + "traefik.tcp.routers.Router0.service": "foobar", + "traefik.tcp.routers.Router0.tls.passthrough": "false", + "traefik.tcp.routers.Router1.rule": "foobar", + "traefik.tcp.routers.Router1.entrypoints": "foobar, fiibar", + "traefik.tcp.routers.Router1.service": "foobar", + "traefik.tcp.routers.Router1.tls.passthrough": "false", + "traefik.tcp.services.Service0.loadbalancer.method": "foobar", + "traefik.tcp.services.Service0.loadbalancer.server.Port": "42", + "traefik.tcp.services.Service0.loadbalancer.server.Weight": "42", + "traefik.tcp.services.Service1.loadbalancer.method": "foobar", + "traefik.tcp.services.Service1.loadbalancer.server.Port": "42", + "traefik.tcp.services.Service1.loadbalancer.server.Weight": "42", } configuration, err := DecodeConfiguration(labels) require.NoError(t, err) - expected := &config.HTTPConfiguration{ - Routers: map[string]*config.Router{ - "Router0": { - EntryPoints: []string{ - "foobar", - "fiibar", - }, - Middlewares: []string{ - "foobar", - "fiibar", - }, - Service: "foobar", - Rule: "foobar", - Priority: 42, - TLS: &config.RouterTLSConfig{}, - }, - "Router1": { - EntryPoints: []string{ - "foobar", - "fiibar", - }, - Middlewares: []string{ - "foobar", - "fiibar", - }, - Service: "foobar", - Rule: "foobar", - Priority: 42, - }, - }, - Middlewares: map[string]*config.Middleware{ - "Middleware0": { - AddPrefix: &config.AddPrefix{ - Prefix: "foobar", - }, - }, - "Middleware1": { - BasicAuth: &config.BasicAuth{ - Users: []string{ - "foobar", - "fiibar", - }, - UsersFile: "foobar", - Realm: "foobar", - RemoveHeader: true, - HeaderField: "foobar", - }, - }, - "Middleware10": { - MaxConn: &config.MaxConn{ - Amount: 42, - ExtractorFunc: "foobar", - }, - }, - "Middleware11": { - PassTLSClientCert: &config.PassTLSClientCert{ - PEM: true, - Info: &config.TLSClientCertificateInfo{ - NotAfter: true, - NotBefore: true, - Subject: &config.TLSCLientCertificateDNInfo{ - Country: true, - Province: true, - Locality: true, - Organization: true, - CommonName: true, - SerialNumber: true, - DomainComponent: true, - }, - Issuer: &config.TLSCLientCertificateDNInfo{ - Country: true, - Province: true, - Locality: true, - Organization: true, - CommonName: true, - SerialNumber: true, - DomainComponent: true, - }, - Sans: true, - }, - }, - }, - "Middleware12": { - RateLimit: &config.RateLimit{ - RateSet: map[string]*config.Rate{ - "Rate0": { - Period: parse.Duration(42 * time.Second), - Average: 42, - Burst: 42, - }, - "Rate1": { - Period: parse.Duration(42 * time.Second), - Average: 42, - Burst: 42, - }, - }, - ExtractorFunc: "foobar", - }, - }, - "Middleware13": { - RedirectRegex: &config.RedirectRegex{ - Regex: "foobar", - Replacement: "foobar", - Permanent: true, - }, - }, - "Middleware13b": { - RedirectScheme: &config.RedirectScheme{ - Scheme: "https", - Port: "80", - Permanent: true, - }, - }, - "Middleware14": { - ReplacePath: &config.ReplacePath{ - Path: "foobar", - }, - }, - "Middleware15": { - ReplacePathRegex: &config.ReplacePathRegex{ - Regex: "foobar", - Replacement: "foobar", - }, - }, - "Middleware16": { - Retry: &config.Retry{ - Attempts: 42, - }, - }, - "Middleware17": { - StripPrefix: &config.StripPrefix{ - Prefixes: []string{ - "foobar", - "fiibar", - }, - }, - }, - "Middleware18": { - StripPrefixRegex: &config.StripPrefixRegex{ - Regex: []string{ - "foobar", - "fiibar", - }, - }, - }, - "Middleware19": { - Compress: &config.Compress{}, - }, - "Middleware2": { - Buffering: &config.Buffering{ - MaxRequestBodyBytes: 42, - MemRequestBodyBytes: 42, - MaxResponseBodyBytes: 42, - MemResponseBodyBytes: 42, - RetryExpression: "foobar", - }, - }, - "Middleware3": { - Chain: &config.Chain{ - Middlewares: []string{ - "foobar", - "fiibar", - }, - }, - }, - "Middleware4": { - CircuitBreaker: &config.CircuitBreaker{ - Expression: "foobar", - }, - }, - "Middleware5": { - DigestAuth: &config.DigestAuth{ - Users: []string{ - "foobar", - "fiibar", - }, - UsersFile: "foobar", - RemoveHeader: true, - Realm: "foobar", - HeaderField: "foobar", - }, - }, - "Middleware6": { - Errors: &config.ErrorPage{ - Status: []string{ + expected := &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{ + "Router0": { + EntryPoints: []string{ "foobar", "fiibar", }, Service: "foobar", - Query: "foobar", - }, - }, - "Middleware7": { - ForwardAuth: &config.ForwardAuth{ - Address: "foobar", - TLS: &config.ClientTLS{ - CA: "foobar", - CAOptional: true, - Cert: "foobar", - Key: "foobar", - InsecureSkipVerify: true, + Rule: "foobar", + TLS: &config.RouterTCPTLSConfig{ + Passthrough: false, }, - TrustForwardHeader: true, - AuthResponseHeaders: []string{ + }, + "Router1": { + EntryPoints: []string{ "foobar", "fiibar", }, + Service: "foobar", + Rule: "foobar", + TLS: &config.RouterTCPTLSConfig{ + Passthrough: false, + }, }, }, - "Middleware8": { - Headers: &config.Headers{ - CustomRequestHeaders: map[string]string{ - "name0": "foobar", - "name1": "foobar", + Services: map[string]*config.TCPService{ + "Service0": { + LoadBalancer: &config.TCPLoadBalancerService{ + Servers: []config.TCPServer{ + { + Port: "42", + Weight: 42, + }, + }, + Method: "foobar", }, - CustomResponseHeaders: map[string]string{ - "name0": "foobar", - "name1": "foobar", + }, + "Service1": { + LoadBalancer: &config.TCPLoadBalancerService{ + Servers: []config.TCPServer{ + { + Port: "42", + Weight: 42, + }, + }, + Method: "foobar", }, - AccessControlAllowCredentials: true, - AccessControlAllowHeaders: []string{ - "X-foobar", - "X-fiibar", - }, - AccessControlAllowMethods: []string{ - "GET", - "PUT", - }, - AccessControlAllowOrigin: "foobar", - AccessControlExposeHeaders: []string{ - "X-foobar", - "X-fiibar", - }, - AccessControlMaxAge: 200, - AddVaryHeader: true, - AllowedHosts: []string{ - "foobar", - "fiibar", - }, - HostsProxyHeaders: []string{ - "foobar", - "fiibar", - }, - SSLRedirect: true, - SSLTemporaryRedirect: true, - SSLHost: "foobar", - SSLProxyHeaders: map[string]string{ - "name0": "foobar", - "name1": "foobar", - }, - SSLForceHost: true, - STSSeconds: 42, - STSIncludeSubdomains: true, - STSPreload: true, - ForceSTSHeader: true, - FrameDeny: true, - CustomFrameOptionsValue: "foobar", - ContentTypeNosniff: true, - BrowserXSSFilter: true, - CustomBrowserXSSValue: "foobar", - ContentSecurityPolicy: "foobar", - PublicKey: "foobar", - ReferrerPolicy: "foobar", - IsDevelopment: true, }, }, - "Middleware9": { - IPWhiteList: &config.IPWhiteList{ - SourceRange: []string{ + }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{ + "Router0": { + EntryPoints: []string{ "foobar", "fiibar", }, - IPStrategy: &config.IPStrategy{ - Depth: 42, - ExcludedIPs: []string{ + Middlewares: []string{ + "foobar", + "fiibar", + }, + Service: "foobar", + Rule: "foobar", + Priority: 42, + TLS: &config.RouterTLSConfig{}, + }, + "Router1": { + EntryPoints: []string{ + "foobar", + "fiibar", + }, + Middlewares: []string{ + "foobar", + "fiibar", + }, + Service: "foobar", + Rule: "foobar", + Priority: 42, + }, + }, + Middlewares: map[string]*config.Middleware{ + "Middleware0": { + AddPrefix: &config.AddPrefix{ + Prefix: "foobar", + }, + }, + "Middleware1": { + BasicAuth: &config.BasicAuth{ + Users: []string{ + "foobar", + "fiibar", + }, + UsersFile: "foobar", + Realm: "foobar", + RemoveHeader: true, + HeaderField: "foobar", + }, + }, + "Middleware10": { + MaxConn: &config.MaxConn{ + Amount: 42, + ExtractorFunc: "foobar", + }, + }, + "Middleware11": { + PassTLSClientCert: &config.PassTLSClientCert{ + PEM: true, + Info: &config.TLSClientCertificateInfo{ + NotAfter: true, + NotBefore: true, + Subject: &config.TLSCLientCertificateDNInfo{ + Country: true, + Province: true, + Locality: true, + Organization: true, + CommonName: true, + SerialNumber: true, + DomainComponent: true, + }, + Issuer: &config.TLSCLientCertificateDNInfo{ + Country: true, + Province: true, + Locality: true, + Organization: true, + CommonName: true, + SerialNumber: true, + DomainComponent: true, + }, + Sans: true, + }, + }, + }, + "Middleware12": { + RateLimit: &config.RateLimit{ + RateSet: map[string]*config.Rate{ + "Rate0": { + Period: parse.Duration(42 * time.Second), + Average: 42, + Burst: 42, + }, + "Rate1": { + Period: parse.Duration(42 * time.Second), + Average: 42, + Burst: 42, + }, + }, + ExtractorFunc: "foobar", + }, + }, + "Middleware13": { + RedirectRegex: &config.RedirectRegex{ + Regex: "foobar", + Replacement: "foobar", + Permanent: true, + }, + }, + "Middleware13b": { + RedirectScheme: &config.RedirectScheme{ + Scheme: "https", + Port: "80", + Permanent: true, + }, + }, + "Middleware14": { + ReplacePath: &config.ReplacePath{ + Path: "foobar", + }, + }, + "Middleware15": { + ReplacePathRegex: &config.ReplacePathRegex{ + Regex: "foobar", + Replacement: "foobar", + }, + }, + "Middleware16": { + Retry: &config.Retry{ + Attempts: 42, + }, + }, + "Middleware17": { + StripPrefix: &config.StripPrefix{ + Prefixes: []string{ "foobar", "fiibar", }, }, }, - }, - }, - Services: map[string]*config.Service{ - "Service0": { - LoadBalancer: &config.LoadBalancerService{ - Stickiness: &config.Stickiness{ - CookieName: "foobar", - }, - Servers: []config.Server{ - { - Scheme: "foobar", - Port: "8080", - Weight: 42, + "Middleware18": { + StripPrefixRegex: &config.StripPrefixRegex{ + Regex: []string{ + "foobar", + "fiibar", }, }, - Method: "foobar", - HealthCheck: &config.HealthCheck{ - Scheme: "foobar", - Path: "foobar", - Port: 42, - Interval: "foobar", - Timeout: "foobar", - Hostname: "foobar", - Headers: map[string]string{ + }, + "Middleware19": { + Compress: &config.Compress{}, + }, + "Middleware2": { + Buffering: &config.Buffering{ + MaxRequestBodyBytes: 42, + MemRequestBodyBytes: 42, + MaxResponseBodyBytes: 42, + MemResponseBodyBytes: 42, + RetryExpression: "foobar", + }, + }, + "Middleware3": { + Chain: &config.Chain{ + Middlewares: []string{ + "foobar", + "fiibar", + }, + }, + }, + "Middleware4": { + CircuitBreaker: &config.CircuitBreaker{ + Expression: "foobar", + }, + }, + "Middleware5": { + DigestAuth: &config.DigestAuth{ + Users: []string{ + "foobar", + "fiibar", + }, + UsersFile: "foobar", + RemoveHeader: true, + Realm: "foobar", + HeaderField: "foobar", + }, + }, + "Middleware6": { + Errors: &config.ErrorPage{ + Status: []string{ + "foobar", + "fiibar", + }, + Service: "foobar", + Query: "foobar", + }, + }, + "Middleware7": { + ForwardAuth: &config.ForwardAuth{ + Address: "foobar", + TLS: &config.ClientTLS{ + CA: "foobar", + CAOptional: true, + Cert: "foobar", + Key: "foobar", + InsecureSkipVerify: true, + }, + TrustForwardHeader: true, + AuthResponseHeaders: []string{ + "foobar", + "fiibar", + }, + }, + }, + "Middleware8": { + Headers: &config.Headers{ + CustomRequestHeaders: map[string]string{ "name0": "foobar", "name1": "foobar", }, + CustomResponseHeaders: map[string]string{ + "name0": "foobar", + "name1": "foobar", + }, + AccessControlAllowCredentials: true, + AccessControlAllowHeaders: []string{ + "X-foobar", + "X-fiibar", + }, + AccessControlAllowMethods: []string{ + "GET", + "PUT", + }, + AccessControlAllowOrigin: "foobar", + AccessControlExposeHeaders: []string{ + "X-foobar", + "X-fiibar", + }, + AccessControlMaxAge: 200, + AddVaryHeader: true, + AllowedHosts: []string{ + "foobar", + "fiibar", + }, + HostsProxyHeaders: []string{ + "foobar", + "fiibar", + }, + SSLRedirect: true, + SSLTemporaryRedirect: true, + SSLHost: "foobar", + SSLProxyHeaders: map[string]string{ + "name0": "foobar", + "name1": "foobar", + }, + SSLForceHost: true, + STSSeconds: 42, + STSIncludeSubdomains: true, + STSPreload: true, + ForceSTSHeader: true, + FrameDeny: true, + CustomFrameOptionsValue: "foobar", + ContentTypeNosniff: true, + BrowserXSSFilter: true, + CustomBrowserXSSValue: "foobar", + ContentSecurityPolicy: "foobar", + PublicKey: "foobar", + ReferrerPolicy: "foobar", + IsDevelopment: true, }, - PassHostHeader: true, - ResponseForwarding: &config.ResponseForwarding{ - FlushInterval: "foobar", + }, + "Middleware9": { + IPWhiteList: &config.IPWhiteList{ + SourceRange: []string{ + "foobar", + "fiibar", + }, + IPStrategy: &config.IPStrategy{ + Depth: 42, + ExcludedIPs: []string{ + "foobar", + "fiibar", + }, + }, }, }, }, - "Service1": { - LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - Scheme: "foobar", - Port: "8080", - Weight: 1, + Services: map[string]*config.Service{ + "Service0": { + LoadBalancer: &config.LoadBalancerService{ + Stickiness: &config.Stickiness{ + CookieName: "foobar", + }, + Servers: []config.Server{ + { + Scheme: "foobar", + Port: "8080", + Weight: 42, + }, + }, + Method: "foobar", + HealthCheck: &config.HealthCheck{ + Scheme: "foobar", + Path: "foobar", + Port: 42, + Interval: "foobar", + Timeout: "foobar", + Hostname: "foobar", + Headers: map[string]string{ + "name0": "foobar", + "name1": "foobar", + }, + }, + PassHostHeader: true, + ResponseForwarding: &config.ResponseForwarding{ + FlushInterval: "foobar", }, }, - Method: "foobar", - HealthCheck: &config.HealthCheck{ - Scheme: "foobar", - Path: "foobar", - Port: 42, - Interval: "foobar", - Timeout: "foobar", - Hostname: "foobar", - Headers: map[string]string{ - "name0": "foobar", - "name1": "foobar", + }, + "Service1": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + Scheme: "foobar", + Port: "8080", + Weight: 1, + }, + }, + Method: "foobar", + HealthCheck: &config.HealthCheck{ + Scheme: "foobar", + Path: "foobar", + Port: 42, + Interval: "foobar", + Timeout: "foobar", + Hostname: "foobar", + Headers: map[string]string{ + "name0": "foobar", + "name1": "foobar", + }, + }, + PassHostHeader: true, + ResponseForwarding: &config.ResponseForwarding{ + FlushInterval: "foobar", }, - }, - PassHostHeader: true, - ResponseForwarding: &config.ResponseForwarding{ - FlushInterval: "foobar", }, }, }, }, } - assert.Equal(t, expected, configuration.HTTP) + assert.Equal(t, expected, configuration) } func TestEncodeConfiguration(t *testing.T) { configuration := &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{ + "Router0": { + EntryPoints: []string{ + "foobar", + "fiibar", + }, + Service: "foobar", + Rule: "foobar", + TLS: &config.RouterTCPTLSConfig{ + Passthrough: false, + }, + }, + "Router1": { + EntryPoints: []string{ + "foobar", + "fiibar", + }, + Service: "foobar", + Rule: "foobar", + TLS: &config.RouterTCPTLSConfig{ + Passthrough: false, + }, + }, + }, + Services: map[string]*config.TCPService{ + "Service0": { + LoadBalancer: &config.TCPLoadBalancerService{ + Servers: []config.TCPServer{ + { + Port: "42", + Weight: 42, + }, + }, + Method: "foobar", + }, + }, + "Service1": { + LoadBalancer: &config.TCPLoadBalancerService{ + Servers: []config.TCPServer{ + { + Port: "42", + Weight: 42, + }, + }, + Method: "foobar", + }, + }, + }, + }, HTTP: &config.HTTPConfiguration{ Routers: map[string]*config.Router{ "Router0": { @@ -1010,6 +1126,21 @@ func TestEncodeConfiguration(t *testing.T) { "traefik.HTTP.Services.Service1.LoadBalancer.server.Scheme": "foobar", "traefik.HTTP.Services.Service0.LoadBalancer.HealthCheck.Headers.name0": "foobar", "traefik.HTTP.Services.Service1.LoadBalancer.server.Weight": "42", + + "traefik.TCP.Routers.Router0.Rule": "foobar", + "traefik.TCP.Routers.Router0.EntryPoints": "foobar, fiibar", + "traefik.TCP.Routers.Router0.Service": "foobar", + "traefik.TCP.Routers.Router0.TLS.Passthrough": "false", + "traefik.TCP.Routers.Router1.Rule": "foobar", + "traefik.TCP.Routers.Router1.EntryPoints": "foobar, fiibar", + "traefik.TCP.Routers.Router1.Service": "foobar", + "traefik.TCP.Routers.Router1.TLS.Passthrough": "false", + "traefik.TCP.Services.Service0.LoadBalancer.Method": "foobar", + "traefik.TCP.Services.Service0.LoadBalancer.server.Port": "42", + "traefik.TCP.Services.Service0.LoadBalancer.server.Weight": "42", + "traefik.TCP.Services.Service1.LoadBalancer.Method": "foobar", + "traefik.TCP.Services.Service1.LoadBalancer.server.Port": "42", + "traefik.TCP.Services.Service1.LoadBalancer.server.Weight": "42", } for key, val := range expected { diff --git a/pkg/provider/marathon/config.go b/pkg/provider/marathon/config.go index 945b7d296..e127b28be 100644 --- a/pkg/provider/marathon/config.go +++ b/pkg/provider/marathon/config.go @@ -39,6 +39,21 @@ func (p *Provider) buildConfiguration(ctx context.Context, applications *maratho continue } + if len(confFromLabel.TCP.Routers) > 0 || len(confFromLabel.TCP.Services) > 0 { + err := p.buildTCPServiceConfiguration(ctxApp, app, extraConf, confFromLabel.TCP) + if err != nil { + logger.Error(err) + continue + } + provider.BuildTCPRouterConfiguration(ctxApp, confFromLabel.TCP) + if len(confFromLabel.HTTP.Routers) == 0 && + len(confFromLabel.HTTP.Middlewares) == 0 && + len(confFromLabel.HTTP.Services) == 0 { + configurations[app.ID] = confFromLabel + continue + } + } + err = p.buildServiceConfiguration(ctxApp, app, extraConf, confFromLabel.HTTP) if err != nil { logger.Error(err) @@ -109,6 +124,48 @@ func (p *Provider) buildServiceConfiguration(ctx context.Context, app marathon.A return nil } +func (p *Provider) buildTCPServiceConfiguration(ctx context.Context, app marathon.Application, extraConf configuration, conf *config.TCPConfiguration) error { + appName := getServiceName(app) + appCtx := log.With(ctx, log.Str("ApplicationID", appName)) + + if len(conf.Services) == 0 { + conf.Services = make(map[string]*config.TCPService) + lb := &config.TCPLoadBalancerService{} + lb.SetDefaults() + conf.Services[appName] = &config.TCPService{ + LoadBalancer: lb, + } + } + + for serviceName, service := range conf.Services { + var servers []config.TCPServer + + defaultServer := config.TCPServer{} + defaultServer.SetDefaults() + + if len(service.LoadBalancer.Servers) > 0 { + defaultServer = service.LoadBalancer.Servers[0] + } + + for _, task := range app.Tasks { + if p.taskFilter(ctx, *task, app) { + server, err := p.getTCPServer(app, *task, extraConf, defaultServer) + if err != nil { + log.FromContext(appCtx).Errorf("Skip task: %v", err) + continue + } + servers = append(servers, server) + } + } + if len(servers) == 0 { + return fmt.Errorf("no server for the service %s", serviceName) + } + service.LoadBalancer.Servers = servers + } + + return nil +} + func (p *Provider) keepApplication(ctx context.Context, extraConf configuration) bool { logger := log.FromContext(ctx) @@ -142,6 +199,25 @@ func (p *Provider) taskFilter(ctx context.Context, task marathon.Task, applicati return true } +func (p *Provider) getTCPServer(app marathon.Application, task marathon.Task, extraConf configuration, defaultServer config.TCPServer) (config.TCPServer, error) { + host, err := p.getServerHost(task, app, extraConf) + if len(host) == 0 { + return config.TCPServer{}, err + } + + port, err := getPort(task, app, defaultServer.Port) + if err != nil { + return config.TCPServer{}, err + } + + server := config.TCPServer{ + Address: net.JoinHostPort(host, port), + Weight: 1, + } + + return server, nil +} + func (p *Provider) getServer(app marathon.Application, task marathon.Task, extraConf configuration, defaultServer config.Server) (config.Server, error) { host, err := p.getServerHost(task, app, extraConf) if len(host) == 0 { diff --git a/pkg/provider/marathon/config_test.go b/pkg/provider/marathon/config_test.go index 058d721f9..03832fe5e 100644 --- a/pkg/provider/marathon/config_test.go +++ b/pkg/provider/marathon/config_test.go @@ -34,7 +34,7 @@ func TestBuildConfiguration(t *testing.T) { constraints types.Constraints filterMarathonConstraints bool defaultRule string - expected *config.HTTPConfiguration + expected *config.Configuration }{ { desc: "simple application", @@ -44,25 +44,31 @@ func TestBuildConfiguration(t *testing.T) { appPorts(80), withTasks(localhostTask(taskPorts(80))), )), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{ - "app": { - Service: "app", - Rule: "Host(`app.marathon.localhost`)", - }, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, }, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{ - "app": {LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, - }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{ + "app": { + Service: "app", + Rule: "Host(`app.marathon.localhost`)", }, - Method: "wrr", - PassHostHeader: true, - }}, + }, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{ + "app": {LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, + }, + Method: "wrr", + PassHostHeader: true, + }}, + }, }, }, }, @@ -74,10 +80,16 @@ func TestBuildConfiguration(t *testing.T) { appPorts(80), withTasks(localhostTask(taskPorts(80), taskState(taskStateStaging))), )), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{}, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{}, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, + }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{}, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{}, + }, }, }, { @@ -88,25 +100,31 @@ func TestBuildConfiguration(t *testing.T) { appPorts(80, 81), withTasks(localhostTask(taskPorts(80, 81))), )), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{ - "app": { - Service: "app", - Rule: "Host(`app.marathon.localhost`)", - }, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, }, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{ - "app": {LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, - }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{ + "app": { + Service: "app", + Rule: "Host(`app.marathon.localhost`)", }, - Method: "wrr", - PassHostHeader: true, - }}, + }, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{ + "app": {LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, + }, + Method: "wrr", + PassHostHeader: true, + }}, + }, }, }, }, @@ -120,35 +138,41 @@ func TestBuildConfiguration(t *testing.T) { withLabel("traefik.http.routers.app.middlewares", "Middleware1"), withTasks(localhostTask(taskPorts(80))), )), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{ - "app": { - Service: "app", - Rule: "Host(`app.marathon.localhost`)", - Middlewares: []string{"Middleware1"}, - }, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, }, - Middlewares: map[string]*config.Middleware{ - "Middleware1": { - BasicAuth: &config.BasicAuth{ - Users: []string{ - "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", - "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{ + "app": { + Service: "app", + Rule: "Host(`app.marathon.localhost`)", + Middlewares: []string{"Middleware1"}, + }, + }, + Middlewares: map[string]*config.Middleware{ + "Middleware1": { + BasicAuth: &config.BasicAuth{ + Users: []string{ + "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", + "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", + }, }, }, }, - }, - Services: map[string]*config.Service{ - "app": {LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, + Services: map[string]*config.Service{ + "app": {LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, }, - }, - Method: "wrr", - PassHostHeader: true, - }}, + Method: "wrr", + PassHostHeader: true, + }}, + }, }, }, }, @@ -170,29 +194,35 @@ func TestBuildConfiguration(t *testing.T) { withLabel("traefik.http.routers.Router1.rule", "Host(`app.marathon.localhost`)"), ), ), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{ - "Router1": { - Service: "Service1", - Rule: "Host(`app.marathon.localhost`)", - }, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, }, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{ - "Service1": {LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:8080", - Weight: 1, - }, - { - URL: "http://localhost:8081", - Weight: 1, - }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{ + "Router1": { + Service: "Service1", + Rule: "Host(`app.marathon.localhost`)", }, - Method: "wrr", - PassHostHeader: true, - }}, + }, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{ + "Service1": {LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:8080", + Weight: 1, + }, + { + URL: "http://localhost:8081", + Weight: 1, + }, + }, + Method: "wrr", + PassHostHeader: true, + }}, + }, }, }, }, @@ -216,37 +246,43 @@ func TestBuildConfiguration(t *testing.T) { withLabel("traefik.http.routers.Router1.rule", "Host(`app.marathon.localhost`)"), ), ), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{ - "Router1": { - Service: "Service1", - Rule: "Host(`app.marathon.localhost`)", - }, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, }, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{ - "Service1": {LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:8080", - Weight: 1, - }, - { - URL: "http://localhost:8081", - Weight: 1, - }, - { - URL: "http://localhost:8082", - Weight: 1, - }, - { - URL: "http://localhost:8083", - Weight: 1, - }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{ + "Router1": { + Service: "Service1", + Rule: "Host(`app.marathon.localhost`)", }, - Method: "wrr", - PassHostHeader: true, - }}, + }, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{ + "Service1": {LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:8080", + Weight: 1, + }, + { + URL: "http://localhost:8081", + Weight: 1, + }, + { + URL: "http://localhost:8082", + Weight: 1, + }, + { + URL: "http://localhost:8083", + Weight: 1, + }, + }, + Method: "wrr", + PassHostHeader: true, + }}, + }, }, }, }, @@ -262,39 +298,45 @@ func TestBuildConfiguration(t *testing.T) { withTasks(localhostTask(taskPorts(8081))), ), ), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{ - "foo": { - Service: "foo", - Rule: "Host(`foo.marathon.localhost`)", - }, - "bar": { - Service: "bar", - Rule: "Host(`bar.marathon.localhost`)", - }, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, }, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{ - "foo": {LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:8080", - Weight: 1, - }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{ + "foo": { + Service: "foo", + Rule: "Host(`foo.marathon.localhost`)", }, - Method: "wrr", - PassHostHeader: true, - }}, - "bar": {LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:8081", - Weight: 1, - }, + "bar": { + Service: "bar", + Rule: "Host(`bar.marathon.localhost`)", }, - Method: "wrr", - PassHostHeader: true, - }}, + }, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{ + "foo": {LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:8080", + Weight: 1, + }, + }, + Method: "wrr", + PassHostHeader: true, + }}, + "bar": {LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:8081", + Weight: 1, + }, + }, + Method: "wrr", + PassHostHeader: true, + }}, + }, }, }, }, @@ -306,29 +348,35 @@ func TestBuildConfiguration(t *testing.T) { appPorts(80), withTasks(localhostTask(taskPorts(80)), localhostTask(taskPorts(81))), )), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{ - "app": { - Service: "app", - Rule: "Host(`app.marathon.localhost`)", - }, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, }, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{ - "app": { - LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, - }, - { - URL: "http://localhost:81", - Weight: 1, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{ + "app": { + Service: "app", + Rule: "Host(`app.marathon.localhost`)", + }, + }, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{ + "app": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, + { + URL: "http://localhost:81", + Weight: 1, + }, }, + Method: "wrr", + PassHostHeader: true, }, - Method: "wrr", - PassHostHeader: true, }, }, }, @@ -343,25 +391,31 @@ func TestBuildConfiguration(t *testing.T) { withTasks(localhostTask(taskPorts(80))), withLabel("traefik.http.services.Service1.loadbalancer.method", "drr"), )), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{ - "app": { - Service: "Service1", - Rule: "Host(`app.marathon.localhost`)", - }, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, }, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{ - "Service1": {LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, - }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{ + "app": { + Service: "Service1", + Rule: "Host(`app.marathon.localhost`)", }, - Method: "drr", - PassHostHeader: true, - }}, + }, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{ + "Service1": {LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, + }, + Method: "drr", + PassHostHeader: true, + }}, + }, }, }, }, @@ -376,25 +430,31 @@ func TestBuildConfiguration(t *testing.T) { withLabel("traefik.http.routers.Router1.rule", "Host(`foo.com`)"), withLabel("traefik.http.routers.Router1.service", "Service1"), )), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{ - "Router1": { - Service: "Service1", - Rule: "Host(`foo.com`)", - }, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, }, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{ - "Service1": { - LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{ + "Router1": { + Service: "Service1", + Rule: "Host(`foo.com`)", + }, + }, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{ + "Service1": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, }, + Method: "wrr", + PassHostHeader: true, }, - Method: "wrr", - PassHostHeader: true, }, }, }, @@ -409,26 +469,32 @@ func TestBuildConfiguration(t *testing.T) { withTasks(localhostTask(taskPorts(80, 81))), withLabel("traefik.http.routers.Router1.rule", "Host(`foo.com`)"), )), - expected: &config.HTTPConfiguration{ - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{ - "app": { - LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, + }, + HTTP: &config.HTTPConfiguration{ + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{ + "app": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, }, + Method: "wrr", + PassHostHeader: true, }, - Method: "wrr", - PassHostHeader: true, }, }, - }, - Routers: map[string]*config.Router{ - "Router1": { - Service: "app", - Rule: "Host(`foo.com`)", + Routers: map[string]*config.Router{ + "Router1": { + Service: "app", + Rule: "Host(`foo.com`)", + }, }, }, }, @@ -443,25 +509,31 @@ func TestBuildConfiguration(t *testing.T) { withLabel("traefik.http.routers.Router1.rule", "Host(`foo.com`)"), withLabel("traefik.http.services.Service1.loadbalancer.method", "wrr"), )), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{ - "Router1": { - Service: "Service1", - Rule: "Host(`foo.com`)", - }, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, }, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{ - "Service1": { - LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{ + "Router1": { + Service: "Service1", + Rule: "Host(`foo.com`)", + }, + }, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{ + "Service1": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, }, + Method: "wrr", + PassHostHeader: true, }, - Method: "wrr", - PassHostHeader: true, }, }, }, @@ -478,32 +550,38 @@ func TestBuildConfiguration(t *testing.T) { withLabel("traefik.http.services.Service1.loadbalancer.method", "wrr"), withLabel("traefik.http.services.Service2.loadbalancer.method", "wrr"), )), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{}, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{ - "Service1": { - LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, + }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{}, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{ + "Service1": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, }, + Method: "wrr", + PassHostHeader: true, }, - Method: "wrr", - PassHostHeader: true, }, - }, - "Service2": { - LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, + "Service2": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, }, + Method: "wrr", + PassHostHeader: true, }, - Method: "wrr", - PassHostHeader: true, }, }, }, @@ -524,19 +602,25 @@ func TestBuildConfiguration(t *testing.T) { withTasks(localhostTask(taskPorts(80, 81))), withLabel("traefik.http.services.Service1.loadbalancer.method", "drr"), )), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{ - "app": { - Service: "Service1", - Rule: "Host(`app.marathon.localhost`)", - }, - "app2": { - Service: "Service1", - Rule: "Host(`app2.marathon.localhost`)", - }, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, + }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{ + "app": { + Service: "Service1", + Rule: "Host(`app.marathon.localhost`)", + }, + "app2": { + Service: "Service1", + Rule: "Host(`app2.marathon.localhost`)", + }, + }, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{}, }, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{}, }, }, { @@ -554,48 +638,54 @@ func TestBuildConfiguration(t *testing.T) { withTasks(localhostTask(taskPorts(80, 81))), withLabel("traefik.http.middlewares.Middleware1.maxconn.amount", "42"), )), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{ - "app": { - Service: "app", - Rule: "Host(`app.marathon.localhost`)", - }, - "app2": { - Service: "app2", - Rule: "Host(`app2.marathon.localhost`)", - }, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, }, - Middlewares: map[string]*config.Middleware{ - "Middleware1": { - MaxConn: &config.MaxConn{ - Amount: 42, - ExtractorFunc: "request.host", + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{ + "app": { + Service: "app", + Rule: "Host(`app.marathon.localhost`)", + }, + "app2": { + Service: "app2", + Rule: "Host(`app2.marathon.localhost`)", }, }, - }, - Services: map[string]*config.Service{ - "app": { - LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, - }, + Middlewares: map[string]*config.Middleware{ + "Middleware1": { + MaxConn: &config.MaxConn{ + Amount: 42, + ExtractorFunc: "request.host", }, - Method: "wrr", - PassHostHeader: true, }, }, - "app2": { - LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, + Services: map[string]*config.Service{ + "app": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, }, + Method: "wrr", + PassHostHeader: true, + }, + }, + "app2": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, + }, + Method: "wrr", + PassHostHeader: true, }, - Method: "wrr", - PassHostHeader: true, }, }, }, @@ -616,41 +706,47 @@ func TestBuildConfiguration(t *testing.T) { withTasks(localhostTask(taskPorts(80, 81))), withLabel("traefik.http.middlewares.Middleware1.maxconn.amount", "41"), )), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{ - "app": { - Service: "app", - Rule: "Host(`app.marathon.localhost`)", - }, - "app2": { - Service: "app2", - Rule: "Host(`app2.marathon.localhost`)", - }, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, }, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{ - "app": { - LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, - }, - }, - Method: "wrr", - PassHostHeader: true, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{ + "app": { + Service: "app", + Rule: "Host(`app.marathon.localhost`)", + }, + "app2": { + Service: "app2", + Rule: "Host(`app2.marathon.localhost`)", }, }, - "app2": { - LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{ + "app": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, }, + Method: "wrr", + PassHostHeader: true, + }, + }, + "app2": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, + }, + Method: "wrr", + PassHostHeader: true, }, - Method: "wrr", - PassHostHeader: true, }, }, }, @@ -671,32 +767,38 @@ func TestBuildConfiguration(t *testing.T) { withTasks(localhostTask(taskPorts(80, 81))), withLabel("traefik.http.routers.Router1.rule", "Host(`bar.com`)"), )), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{}, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{ - "app": { - LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, + }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{}, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{ + "app": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, }, + Method: "wrr", + PassHostHeader: true, }, - Method: "wrr", - PassHostHeader: true, }, - }, - "app2": { - LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, + "app2": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, }, + Method: "wrr", + PassHostHeader: true, }, - Method: "wrr", - PassHostHeader: true, }, }, }, @@ -719,29 +821,35 @@ func TestBuildConfiguration(t *testing.T) { withLabel("traefik.http.routers.Router1.rule", "Host(`foo.com`)"), withLabel("traefik.http.services.Service1.LoadBalancer.method", "wrr"), )), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{ - "Router1": { - Service: "Service1", - Rule: "Host(`foo.com`)", - }, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, }, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{ - "Service1": { - LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, - }, - { - URL: "http://localhost:80", - Weight: 1, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{ + "Router1": { + Service: "Service1", + Rule: "Host(`foo.com`)", + }, + }, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{ + "Service1": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, + { + URL: "http://localhost:80", + Weight: 1, + }, }, + Method: "wrr", + PassHostHeader: true, }, - Method: "wrr", - PassHostHeader: true, }, }, }, @@ -762,32 +870,38 @@ func TestBuildConfiguration(t *testing.T) { withTasks(localhostTask(taskPorts(80, 81))), withLabel("traefik.http.routers.Router1.rule", "Host(`foo.com`)"), )), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{}, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{ - "app": { - LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, + }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{}, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{ + "app": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, }, + Method: "wrr", + PassHostHeader: true, }, - Method: "wrr", - PassHostHeader: true, }, - }, - "app2": { - LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, + "app2": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, }, + Method: "wrr", + PassHostHeader: true, }, - Method: "wrr", - PassHostHeader: true, }, }, }, @@ -802,25 +916,31 @@ func TestBuildConfiguration(t *testing.T) { withTasks(localhostTask(taskPorts(80, 81))), withLabel("traefik.wrong.label", "tchouk"), )), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{ - "app": { - Service: "app", - Rule: "Host(`app.marathon.localhost`)", - }, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, }, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{ - "app": { - LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{ + "app": { + Service: "app", + Rule: "Host(`app.marathon.localhost`)", + }, + }, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{ + "app": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, }, + Method: "wrr", + PassHostHeader: true, }, - Method: "wrr", - PassHostHeader: true, }, }, }, @@ -836,25 +956,31 @@ func TestBuildConfiguration(t *testing.T) { withLabel("traefik.http.services.Service1.LoadBalancer.server.scheme", "h2c"), withLabel("traefik.http.services.Service1.LoadBalancer.server.port", "90"), )), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{ - "app": { - Service: "Service1", - Rule: "Host(`app.marathon.localhost`)", - }, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, }, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{ - "Service1": { - LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "h2c://localhost:90", - Weight: 1, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{ + "app": { + Service: "Service1", + Rule: "Host(`app.marathon.localhost`)", + }, + }, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{ + "Service1": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "h2c://localhost:90", + Weight: 1, + }, }, + Method: "wrr", + PassHostHeader: true, }, - Method: "wrr", - PassHostHeader: true, }, }, }, @@ -870,32 +996,38 @@ func TestBuildConfiguration(t *testing.T) { withLabel("traefik.http.services.Service1.LoadBalancer.server.port", ""), withLabel("traefik.http.services.Service2.LoadBalancer.server.port", "8080"), )), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{}, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{ - "Service1": { - LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, + }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{}, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{ + "Service1": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, }, + Method: "wrr", + PassHostHeader: true, }, - Method: "wrr", - PassHostHeader: true, }, - }, - "Service2": { - LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:8080", - Weight: 1, + "Service2": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:8080", + Weight: 1, + }, }, + Method: "wrr", + PassHostHeader: true, }, - Method: "wrr", - PassHostHeader: true, }, }, }, @@ -909,10 +1041,16 @@ func TestBuildConfiguration(t *testing.T) { appPorts(80, 81), withTasks(localhostTask()), )), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{}, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{}, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, + }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{}, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{}, + }, }, }, { @@ -924,10 +1062,16 @@ func TestBuildConfiguration(t *testing.T) { withTasks(localhostTask()), withLabel("traefik.http.middlewares.Middleware1.basicauth.users", "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), )), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{}, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{}, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, + }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{}, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{}, + }, }, }, { @@ -939,10 +1083,16 @@ func TestBuildConfiguration(t *testing.T) { withTasks(localhostTask()), withLabel("traefik.enable", "false"), )), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{}, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{}, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, + }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{}, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{}, + }, }, }, { @@ -954,10 +1104,16 @@ func TestBuildConfiguration(t *testing.T) { withTasks(localhostTask()), withLabel("traefik.enable", "false"), )), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{}, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{}, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, + }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{}, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{}, + }, }, }, { @@ -976,10 +1132,16 @@ func TestBuildConfiguration(t *testing.T) { Regex: "bar", }, }, - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{}, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{}, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, + }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{}, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{}, + }, }, }, { @@ -999,10 +1161,16 @@ func TestBuildConfiguration(t *testing.T) { Regex: "rack_id:CLUSTER:rack-2", }, }, - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{}, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{}, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, + }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{}, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{}, + }, }, }, { @@ -1022,25 +1190,31 @@ func TestBuildConfiguration(t *testing.T) { Regex: "rack_id:CLUSTER:rack-1", }, }, - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{ - "app": { - Service: "app", - Rule: "Host(`app.marathon.localhost`)", - }, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, }, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{ - "app": { - LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{ + "app": { + Service: "app", + Rule: "Host(`app.marathon.localhost`)", + }, + }, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{ + "app": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, }, + Method: "wrr", + PassHostHeader: true, }, - Method: "wrr", - PassHostHeader: true, }, }, }, @@ -1063,25 +1237,31 @@ func TestBuildConfiguration(t *testing.T) { Regex: "bar", }, }, - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{ - "app": { - Service: "app", - Rule: "Host(`app.marathon.localhost`)", - }, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, }, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{ - "app": { - LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{ + "app": { + Service: "app", + Rule: "Host(`app.marathon.localhost`)", + }, + }, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{ + "app": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, }, + Method: "wrr", + PassHostHeader: true, }, - Method: "wrr", - PassHostHeader: true, }, }, }, @@ -1096,25 +1276,205 @@ func TestBuildConfiguration(t *testing.T) { appPorts(80, 81), withTasks(localhostTask(taskPorts(80, 81))), )), - expected: &config.HTTPConfiguration{ - Routers: map[string]*config.Router{ - "a_b_app": { - Service: "a_b_app", - Rule: `Host("app.b.a.marathon.localhost")`, + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{}, + }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{ + "a_b_app": { + Service: "a_b_app", + Rule: `Host("app.b.a.marathon.localhost")`, + }, + }, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{ + "a_b_app": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, + }, + Method: "wrr", + PassHostHeader: true, + }, + }, }, }, - Middlewares: map[string]*config.Middleware{}, - Services: map[string]*config.Service{ - "a_b_app": { - LoadBalancer: &config.LoadBalancerService{ - Servers: []config.Server{ - { - URL: "http://localhost:80", - Weight: 1, + }, + }, + { + desc: "one app with tcp labels", + applications: withApplications( + application( + appID("/app"), + appPorts(80, 81), + withTasks(localhostTask(taskPorts(80, 81))), + withLabel("traefik.tcp.routers.foo.rule", "HostSNI(`foo.bar`)"), + withLabel("traefik.tcp.routers.foo.tls", "true"), + )), + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{ + "foo": { + Service: "app", + Rule: "HostSNI(`foo.bar`)", + TLS: &config.RouterTCPTLSConfig{}, + }, + }, + Services: map[string]*config.TCPService{ + "app": { + LoadBalancer: &config.TCPLoadBalancerService{ + Servers: []config.TCPServer{ + { + Address: "localhost:80", + Weight: 1, + }, }, + Method: "wrr", + }, + }, + }, + }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{}, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{}, + }, + }, + }, + { + desc: "one app with tcp labels without rule", + applications: withApplications( + application( + appID("/app"), + appPorts(80, 81), + withTasks(localhostTask(taskPorts(80, 81))), + withLabel("traefik.tcp.routers.foo.tls", "true"), + )), + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{}, + Services: map[string]*config.TCPService{ + "app": { + LoadBalancer: &config.TCPLoadBalancerService{ + Servers: []config.TCPServer{ + { + Address: "localhost:80", + Weight: 1, + }, + }, + Method: "wrr", + }, + }, + }, + }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{}, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{}, + }, + }, + }, + { + desc: "one app with tcp labels with port", + applications: withApplications( + application( + appID("/app"), + appPorts(80, 81), + withTasks(localhostTask(taskPorts(80, 81))), + withLabel("traefik.tcp.routers.foo.rule", "HostSNI(`foo.bar`)"), + withLabel("traefik.tcp.routers.foo.tls", "true"), + withLabel("traefik.tcp.services.foo.loadbalancer.server.port", "8080"), + )), + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{ + "foo": { + Service: "foo", + Rule: "HostSNI(`foo.bar`)", + TLS: &config.RouterTCPTLSConfig{}, + }, + }, + Services: map[string]*config.TCPService{ + "foo": { + LoadBalancer: &config.TCPLoadBalancerService{ + Servers: []config.TCPServer{ + { + Address: "localhost:8080", + Weight: 1, + }, + }, + Method: "wrr", + }, + }, + }, + }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{}, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{}, + }, + }, + }, + { + desc: "one app with tcp labels with port and http service", + applications: withApplications( + application( + appID("/app"), + appPorts(80, 81), + withTasks(localhostTask(taskPorts(80, 81))), + withLabel("traefik.tcp.routers.foo.rule", "HostSNI(`foo.bar`)"), + withLabel("traefik.tcp.routers.foo.tls", "true"), + withLabel("traefik.tcp.services.foo.loadbalancer.server.port", "8080"), + withLabel("traefik.http.services.bar.loadbalancer.method", "drr"), + )), + expected: &config.Configuration{ + TCP: &config.TCPConfiguration{ + Routers: map[string]*config.TCPRouter{ + "foo": { + Service: "foo", + Rule: "HostSNI(`foo.bar`)", + TLS: &config.RouterTCPTLSConfig{}, + }, + }, + Services: map[string]*config.TCPService{ + "foo": { + LoadBalancer: &config.TCPLoadBalancerService{ + Servers: []config.TCPServer{ + { + Address: "localhost:8080", + Weight: 1, + }, + }, + Method: "wrr", + }, + }, + }, + }, + HTTP: &config.HTTPConfiguration{ + Routers: map[string]*config.Router{ + "app": { + Service: "bar", + Rule: "Host(`app.marathon.localhost`)", + }, + }, + Middlewares: map[string]*config.Middleware{}, + Services: map[string]*config.Service{ + "bar": { + LoadBalancer: &config.LoadBalancerService{ + Servers: []config.Server{ + { + URL: "http://localhost:80", + Weight: 1, + }, + }, + Method: "drr", + PassHostHeader: true, }, - Method: "wrr", - PassHostHeader: true, }, }, }, @@ -1145,7 +1505,7 @@ func TestBuildConfiguration(t *testing.T) { actualConfig := p.buildConfiguration(context.Background(), test.applications) assert.NotNil(t, actualConfig) - assert.Equal(t, test.expected, actualConfig.HTTP) + assert.Equal(t, test.expected, actualConfig) }) } }