From 7be2db6e866c0b0eda2d664de3e23c2772b223e0 Mon Sep 17 00:00:00 2001 From: Andrew Privalov Date: Tue, 15 Oct 2019 18:34:08 +0300 Subject: [PATCH] Add Consul Catalog provider --- docs/content/assets/img/providers/consul.png | Bin 0 -> 14864 bytes docs/content/middlewares/addprefix.md | 5 + docs/content/middlewares/basicauth.md | 25 + docs/content/middlewares/buffering.md | 25 + docs/content/middlewares/chain.md | 11 + docs/content/middlewares/circuitbreaker.md | 5 + docs/content/middlewares/compress.md | 5 + docs/content/middlewares/digestauth.md | 31 +- docs/content/middlewares/errorpages.md | 7 + docs/content/middlewares/forwardauth.md | 39 + docs/content/middlewares/headers.md | 21 + docs/content/middlewares/inflightreq.md | 44 +- docs/content/middlewares/ipwhitelist.md | 30 +- docs/content/middlewares/overview.md | 7 + docs/content/middlewares/passtlsclientcert.md | 64 +- docs/content/middlewares/ratelimit.md | 48 +- docs/content/middlewares/redirectregex.md | 7 + docs/content/middlewares/redirectscheme.md | 6 + docs/content/middlewares/replacepath.md | 5 + docs/content/middlewares/replacepathregex.md | 6 + docs/content/middlewares/retry.md | 5 + docs/content/middlewares/stripprefix.md | 5 + docs/content/middlewares/stripprefixregex.md | 4 + docs/content/operations/api.md | 8 + docs/content/providers/consul-catalog.md | 273 +++ docs/content/providers/consul-catalog.toml | 44 + docs/content/providers/consul-catalog.txt | 53 + docs/content/providers/consul-catalog.yml | 28 + .../dynamic-configuration/consul-catalog.md | 11 + .../dynamic-configuration/consul-catalog.yml | 1 + .../reference/static-configuration/cli-ref.md | 51 + .../reference/static-configuration/env-ref.md | 51 + .../reference/static-configuration/file.toml | 18 + .../reference/static-configuration/file.yaml | 18 + .../routing/providers/consul-catalog.md | 388 ++++ docs/mkdocs.yml | 7 +- go.mod | 5 +- go.sum | 93 +- integration/consul_catalog_test.go | 342 +++ .../consul_catalog/default_not_exposed.toml | 20 + .../fixtures/consul_catalog/simple.toml | 21 + integration/integration_test.go | 1 + .../resources/compose/consul_catalog.yml | 16 + pkg/config/static/static_config.go | 18 +- pkg/provider/aggregator/aggregator.go | 4 + pkg/provider/consulcatalog/config.go | 200 ++ pkg/provider/consulcatalog/config_test.go | 1850 +++++++++++++++++ pkg/provider/consulcatalog/consul_catalog.go | 221 ++ pkg/provider/consulcatalog/convert_types.go | 26 + .../consulcatalog/convert_types_test.go | 64 + pkg/provider/consulcatalog/label.go | 23 + webui/src/statics/providers/consulcatalog.svg | 1 + 52 files changed, 4194 insertions(+), 67 deletions(-) create mode 100644 docs/content/assets/img/providers/consul.png create mode 100644 docs/content/providers/consul-catalog.md create mode 100644 docs/content/providers/consul-catalog.toml create mode 100644 docs/content/providers/consul-catalog.txt create mode 100644 docs/content/providers/consul-catalog.yml create mode 100644 docs/content/reference/dynamic-configuration/consul-catalog.md create mode 100644 docs/content/reference/dynamic-configuration/consul-catalog.yml create mode 100644 docs/content/routing/providers/consul-catalog.md create mode 100644 integration/consul_catalog_test.go create mode 100644 integration/fixtures/consul_catalog/default_not_exposed.toml create mode 100644 integration/fixtures/consul_catalog/simple.toml create mode 100644 integration/resources/compose/consul_catalog.yml create mode 100644 pkg/provider/consulcatalog/config.go create mode 100644 pkg/provider/consulcatalog/config_test.go create mode 100644 pkg/provider/consulcatalog/consul_catalog.go create mode 100644 pkg/provider/consulcatalog/convert_types.go create mode 100644 pkg/provider/consulcatalog/convert_types_test.go create mode 100644 pkg/provider/consulcatalog/label.go create mode 100644 webui/src/statics/providers/consulcatalog.svg diff --git a/docs/content/assets/img/providers/consul.png b/docs/content/assets/img/providers/consul.png new file mode 100644 index 0000000000000000000000000000000000000000..23a7688c95d721c1d01af5e36a30f0874116784f GIT binary patch literal 14864 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 literal 0 HcmV?d00001 diff --git a/docs/content/middlewares/addprefix.md b/docs/content/middlewares/addprefix.md index c41cd1d40..909c11335 100644 --- a/docs/content/middlewares/addprefix.md +++ b/docs/content/middlewares/addprefix.md @@ -26,6 +26,11 @@ spec: prefix: /foo ``` +```yaml tab="Consul Catalog" +# Prefixing with /foo +- "traefik.http.middlewares.add-foo.addprefix.prefix=/foo" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.add-foo.addprefix.prefix": "/foo" diff --git a/docs/content/middlewares/basicauth.md b/docs/content/middlewares/basicauth.md index 3225457ec..60b8cf9a1 100644 --- a/docs/content/middlewares/basicauth.md +++ b/docs/content/middlewares/basicauth.md @@ -30,6 +30,10 @@ spec: secret: secretName ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-auth.basicauth.users=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,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" @@ -115,6 +119,11 @@ data: aHI5SEJCJDRIeHdnVWlyM0hQNEVzZ2dQL1FObzAK ``` +```yaml tab="Consul Catalog" +# Declaring the user list +- "traefik.http.middlewares.test-auth.basicauth.users=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,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" @@ -186,6 +195,10 @@ data: aHI5SEJCJDRIeHdnVWlyM0hQNEVzZ2dQL1FObzAK ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-auth.basicauth.usersfile=/path/to/my/usersfile" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-auth.basicauth.usersfile": "/path/to/my/usersfile" @@ -237,6 +250,10 @@ spec: realm: MyRealm ``` +```json tab="Consul Catalog" +- "traefik.http.middlewares.test-auth.basicauth.realm=MyRealm" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-auth.basicauth.realm": "MyRealm" @@ -282,6 +299,10 @@ spec: headerField: X-WebAuth-User ``` +```json tab="Consul Catalog" +- "traefik.http.middlewares.my-auth.basicauth.headerField=X-WebAuth-User" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.my-auth.basicauth.headerField": "X-WebAuth-User" @@ -322,6 +343,10 @@ spec: removeHeader: true ``` +```json tab="Consul Catalog" +- "traefik.http.middlewares.test-auth.basicauth.removeheader=true" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-auth.basicauth.removeheader": "true" diff --git a/docs/content/middlewares/buffering.md b/docs/content/middlewares/buffering.md index c30a1054b..ced3878d5 100644 --- a/docs/content/middlewares/buffering.md +++ b/docs/content/middlewares/buffering.md @@ -30,6 +30,11 @@ spec: maxRequestBodyBytes: 2000000 ``` +```yaml tab="Consul Catalog" +# Sets the maximum request body to 2Mb +- "traefik.http.middlewares.limit.buffering.maxRequestBodyBytes=2000000" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.limit.buffering.maxRequestBodyBytes": "2000000" @@ -81,6 +86,10 @@ spec: maxRequestBodyBytes: 2000000 ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.limit.buffering.maxRequestBodyBytes=2000000" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.limit.buffering.maxRequestBodyBytes": "2000000" @@ -125,6 +134,10 @@ spec: memRequestBodyBytes: 2000000 ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.limit.buffering.memRequestBodyBytes=2000000" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.limit.buffering.memRequestBodyBytes": "2000000" @@ -171,6 +184,10 @@ spec: maxResponseBodyBytes: 2000000 ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.limit.buffering.maxResponseBodyBytes=2000000" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.limit.buffering.maxResponseBodyBytes": "2000000" @@ -215,6 +232,10 @@ spec: memResponseBodyBytes: 2000000 ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.limit.buffering.memResponseBodyBytes=2000000" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.limit.buffering.memResponseBodyBytes": "2000000" @@ -261,6 +282,10 @@ You can have the Buffering middleware replay the request with the help of the `r retryExpression: "IsNetworkError() && Attempts() < 2" ``` + ```yaml tab="Consul Catalog" + - "traefik.http.middlewares.limit.buffering.retryExpression=IsNetworkError() && Attempts() < 2" + ``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.limit.buffering.retryExpression": "IsNetworkError() && Attempts() < 2" diff --git a/docs/content/middlewares/chain.md b/docs/content/middlewares/chain.md index 5afb07cd9..b76402d0e 100644 --- a/docs/content/middlewares/chain.md +++ b/docs/content/middlewares/chain.md @@ -83,6 +83,17 @@ spec: - 127.0.0.1/32 ``` +```yaml tab="Consul Catalog" +- "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.redirectscheme.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" +``` + ```json tab="Marathon" "labels": { "traefik.http.routers.router1.service": "service1", diff --git a/docs/content/middlewares/circuitbreaker.md b/docs/content/middlewares/circuitbreaker.md index 8d64cd427..49d911101 100644 --- a/docs/content/middlewares/circuitbreaker.md +++ b/docs/content/middlewares/circuitbreaker.md @@ -45,6 +45,11 @@ spec: expression: LatencyAtQuantileMS(50.0) > 100 ``` +```yaml tab="Consul Catalog" +# Latency Check +- "traefik.http.middlewares.latency-check.circuitbreaker.expression=LatencyAtQuantileMS(50.0) > 100" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.latency-check.circuitbreaker.expression": "LatencyAtQuantileMS(50.0) > 100" diff --git a/docs/content/middlewares/compress.md b/docs/content/middlewares/compress.md index f17457526..b2e61f185 100644 --- a/docs/content/middlewares/compress.md +++ b/docs/content/middlewares/compress.md @@ -25,6 +25,11 @@ spec: compress: {} ``` +```yaml tab="Consul Catalog" +# Enable gzip compression +- "traefik.http.middlewares.test-compress.compress=true" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-compress.compress": "true" diff --git a/docs/content/middlewares/digestauth.md b/docs/content/middlewares/digestauth.md index ce8eab6c1..0ddfd151a 100644 --- a/docs/content/middlewares/digestauth.md +++ b/docs/content/middlewares/digestauth.md @@ -26,6 +26,11 @@ spec: secret: userssecret ``` +```yaml tab="Consul Catalog" +# Declaring the user list +- "traefik.http.middlewares.test-auth.digestauth.users=test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-auth.digestauth.users": "test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e" @@ -100,6 +105,10 @@ data: dGVzdDp0cmFlZmlrOmEyNjg4ZTAzMWVkYjRiZTZhMzc5N2YzODgyNjU1YzA1CnRlc3QyOnRyYWVmaWs6NTE4ODQ1ODAwZjllMmJmYjFmMWY3NDBlYzI0ZjA3NGUKCg== ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-auth.digestauth.users=test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-auth.digestauth.users": "test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e" @@ -168,6 +177,10 @@ data: aHI5SEJCJDRIeHdnVWlyM0hQNEVzZ2dQL1FObzAK ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-auth.digestauth.usersfile=/path/to/my/usersfile" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-auth.digestauth.usersfile": "/path/to/my/usersfile" @@ -219,6 +232,10 @@ spec: realm: MyRealm ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-auth.digestauth.realm=MyRealm" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-auth.digestauth.realm": "MyRealm" @@ -264,9 +281,8 @@ spec: headerField: X-WebAuth-User ``` -```yaml tab="Rancher" -labels: - - "traefik.http.middlewares.my-auth.digestauth.headerField=X-WebAuth-User" +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.my-auth.digestauth.headerField=X-WebAuth-User" ``` ```json tab="Marathon" @@ -275,6 +291,11 @@ labels: } ``` +```yaml tab="Rancher" +labels: + - "traefik.http.middlewares.my-auth.digestauth.headerField=X-WebAuth-User" +``` + ```toml tab="File (TOML)" [http.middlewares.my-auth.digestAuth] # ... @@ -309,6 +330,10 @@ spec: removeHeader: true ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-auth.digestauth.removeheader=true" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-auth.digestauth.removeheader": "true" diff --git a/docs/content/middlewares/errorpages.md b/docs/content/middlewares/errorpages.md index 8fde1a01f..86b095ee0 100644 --- a/docs/content/middlewares/errorpages.md +++ b/docs/content/middlewares/errorpages.md @@ -35,6 +35,13 @@ spec: port: 80 ``` +```yaml tab="Consul Catalog" +# Dynamic Custom Error Page for 5XX Status Code +- "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" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-errorpage.errors.status": "500-599", diff --git a/docs/content/middlewares/forwardauth.md b/docs/content/middlewares/forwardauth.md index fd8324756..39393e277 100644 --- a/docs/content/middlewares/forwardauth.md +++ b/docs/content/middlewares/forwardauth.md @@ -28,6 +28,11 @@ spec: address: https://authserver.com/auth ``` +```yaml tab="Consul Catalog" +# Forward authentication to authserver.com +- "traefik.http.middlewares.test-auth.forwardauth.address=https://authserver.com/auth" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-auth.forwardauth.address": "https://authserver.com/auth" @@ -77,6 +82,10 @@ spec: address: https://authserver.com/auth ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-auth.forwardauth.address=https://authserver.com/auth" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-auth.forwardauth.address": "https://authserver.com/auth" @@ -122,6 +131,10 @@ spec: trustForwardHeader: true ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-auth.forwardauth.trustForwardHeader=true" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-auth.forwardauth.trustForwardHeader": "true" @@ -171,6 +184,10 @@ spec: - X-Secret ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-auth.forwardauth.authResponseHeaders=X-Auth-User, X-Secret" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-auth.forwardauth.authResponseHeaders": "X-Auth-User,X-Secret" @@ -235,6 +252,10 @@ data: ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-auth.forwardauth.tls.ca=path/to/local.crt" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-auth.forwardauth.tls.ca": "path/to/local.crt" @@ -290,6 +311,10 @@ spec: caOptional: true ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-auth.forwardauth.tls.caOptional=true" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-auth.forwardauth.tls.caOptional": "true" @@ -352,6 +377,11 @@ data: tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-auth.forwardauth.tls.cert=path/to/foo.cert" +- "traefik.http.middlewares.test-auth.forwardauth.tls.key=path/to/foo.key" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-auth.forwardauth.tls.cert": "path/to/foo.cert", @@ -421,6 +451,11 @@ data: tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-auth.forwardauth.tls.cert=path/to/foo.cert" +- "traefik.http.middlewares.test-auth.forwardauth.tls.key=path/to/foo.key" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-auth.forwardauth.tls.cert": "path/to/foo.cert", @@ -477,6 +512,10 @@ spec: insecureSkipVerify: true ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-auth.forwardauth.tls.InsecureSkipVerify=true" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-auth.forwardauth.tls.insecureSkipVerify": "true" diff --git a/docs/content/middlewares/headers.md b/docs/content/middlewares/headers.md index 20fdffcc8..708cf6d08 100644 --- a/docs/content/middlewares/headers.md +++ b/docs/content/middlewares/headers.md @@ -32,6 +32,11 @@ spec: X-Custom-Response-Header: "value" ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.testheader.headers.customrequestheaders.X-Script-Name=test" +- "traefik.http.middlewares.testheader.headers.customresponseheaders.X-Custom-Response-Header=value" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.testheader.headers.customrequestheaders.X-Script-Name": "test", @@ -91,6 +96,10 @@ spec: X-Custom-Response-Header: "" # Removes ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.testheader.headers.customrequestheaders.X-Script-Name=test" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.testheader.headers.customrequestheaders.X-Script-Name": "test", @@ -146,6 +155,11 @@ spec: sslRedirect: "true" ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.testheader.headers.framedeny=true" +- "traefik.http.middlewares.testheader.headers.sslredirect=true" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.testheader.headers.framedeny": "true", @@ -204,6 +218,13 @@ spec: addVaryHeader: "true" ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.testheader.headers.accesscontrolallowmethods=GET,OPTIONS,PUT" +- "traefik.http.middlewares.testheader.headers.accesscontrolalloworigin=origin-list-or-null" +- "traefik.http.middlewares.testheader.headers.accesscontrolmaxage=100" +- "traefik.http.middlewares.testheader.headers.addvaryheader=true" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.testheader.headers.accesscontrolallowmethods": "GET,OPTIONS,PUT", diff --git a/docs/content/middlewares/inflightreq.md b/docs/content/middlewares/inflightreq.md index 5f0d2623a..8a040d022 100644 --- a/docs/content/middlewares/inflightreq.md +++ b/docs/content/middlewares/inflightreq.md @@ -24,6 +24,11 @@ spec: amount: 10 ``` +```yaml tab="Consul Catalog" +# Limiting to 10 simultaneous connections +- "traefik.http.middlewares.test-inflightreq.inflightreq.amount=10" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-inflightreq.inflightreq.amount": "10" @@ -74,6 +79,11 @@ spec: amount: 10 ``` +```yaml tab="Consul Catalog" +# Limiting to 10 simultaneous connections +- "traefik.http.middlewares.test-inflightreq.inflightreq.amount=10" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-inflightreq.inflightreq.amount": "10" @@ -146,9 +156,8 @@ spec: depth: 2 ``` -```yaml tab="Rancher" -labels: - - "traefik.http.middlewares.test-inflightreq.inflightreq.sourcecriterion.ipstrategy.depth=2" +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-inflightreq.inflightreq.sourcecriterion.ipstrategy.depth=2" ``` ```json tab="Marathon" @@ -157,6 +166,11 @@ labels: } ``` +```yaml tab="Rancher" +labels: + - "traefik.http.middlewares.test-inflightreq.inflightreq.sourcecriterion.ipstrategy.depth=2" +``` + ```toml tab="File (TOML)" [http.middlewares] [http.middlewares.test-inflightreq.inflightreq] @@ -209,6 +223,10 @@ spec: - 192.168.1.7 ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-inflightreq.inflightreq.sourcecriterion.ipstrategy.excludedips=127.0.0.1/32, 192.168.1.7" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-inflightreq.inflightreq.sourcecriterion.ipstrategy.excludedips": "127.0.0.1/32, 192.168.1.7" @@ -259,9 +277,8 @@ spec: requestHeaderName: username ``` -```yaml tab="Rancher" -labels: - - "traefik.http.middlewares.test-inflightreq.inflightreq.sourcecriterion.requestheadername=username" +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-inflightreq.inflightreq.sourcecriterion.requestheadername=username" ``` ```json tab="Marathon" @@ -270,6 +287,11 @@ labels: } ``` +```yaml tab="Rancher" +labels: + - "traefik.http.middlewares.test-inflightreq.inflightreq.sourcecriterion.requestheadername=username" +``` + ```toml tab="File (TOML)" [http.middlewares] [http.middlewares.test-inflightreq.inflightreq] @@ -306,9 +328,8 @@ spec: requestHost: true ``` -```yaml tab="Rancher" -labels: - - "traefik.http.middlewares.test-inflightreq.inflightreq.sourcecriterion.requesthost=true" +```yaml tab="Cosul Catalog" +- "traefik.http.middlewares.test-inflightreq.inflightreq.sourcecriterion.requesthost=true" ``` ```json tab="Marathon" @@ -317,6 +338,11 @@ labels: } ``` +```yaml tab="Rancher" +labels: + - "traefik.http.middlewares.test-inflightreq.inflightreq.sourcecriterion.requesthost=true" +``` + ```toml tab="File (TOML)" [http.middlewares] [http.middlewares.test-inflightreq.inflightreq] diff --git a/docs/content/middlewares/ipwhitelist.md b/docs/content/middlewares/ipwhitelist.md index 271a7b123..79706d59d 100644 --- a/docs/content/middlewares/ipwhitelist.md +++ b/docs/content/middlewares/ipwhitelist.md @@ -27,6 +27,11 @@ spec: - 192.168.1.7 ``` +```yaml tab="Consul Catalog" +# Accepts request from defined IP +- "traefik.http.middlewares.test-ipwhitelist.ipwhitelist.sourcerange=127.0.0.1/32, 192.168.1.7" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-ipwhitelist.ipwhitelist.sourcerange": "127.0.0.1/32,192.168.1.7" @@ -95,11 +100,10 @@ The `depth` option tells Traefik to use the `X-Forwarded-For` header and take th depth: 2 ``` - ```yaml tab="Rancher" + ```yaml tab="Consul Catalog" # Whitelisting Based on `X-Forwarded-For` with `depth=2` - labels: - - "traefik.http.middlewares.testIPwhitelist.ipwhitelist.sourcerange=127.0.0.1/32, 192.168.1.7" - - "traefik.http.middlewares.testIPwhitelist.ipwhitelist.ipstrategy.depth=2" + - "traefik.http.middlewares.testIPwhitelist.ipwhitelist.sourcerange=127.0.0.1/32, 192.168.1.7" + - "traefik.http.middlewares.testIPwhitelist.ipwhitelist.ipstrategy.depth=2" ``` ```json tab="Marathon" @@ -109,6 +113,13 @@ The `depth` option tells Traefik to use the `X-Forwarded-For` header and take th } ``` + ```yaml tab="Rancher" + # Whitelisting Based on `X-Forwarded-For` with `depth=2` + labels: + - "traefik.http.middlewares.testIPwhitelist.ipwhitelist.sourcerange=127.0.0.1/32, 192.168.1.7" + - "traefik.http.middlewares.testIPwhitelist.ipwhitelist.ipstrategy.depth=2" + ``` + ```toml tab="File (TOML)" # Whitelisting Based on `X-Forwarded-For` with `depth=2` [http.middlewares] @@ -168,10 +179,9 @@ spec: - 192.168.1.7 ``` -```yaml tab="Rancher" +```yaml tab="Consul Catalog" # Exclude from `X-Forwarded-For` -labels: - - "traefik.http.middlewares.test-ipwhitelist.ipwhitelist.ipstrategy.excludedips=127.0.0.1/32, 192.168.1.7" +- "traefik.http.middlewares.test-ipwhitelist.ipwhitelist.ipstrategy.excludedips=127.0.0.1/32, 192.168.1.7" ``` ```json tab="Marathon" @@ -180,6 +190,12 @@ labels: } ``` +```yaml tab="Rancher" +# Exclude from `X-Forwarded-For` +labels: + - "traefik.http.middlewares.test-ipwhitelist.ipwhitelist.ipstrategy.excludedips=127.0.0.1/32, 192.168.1.7" +``` + ```toml tab="File (TOML)" # Exclude from `X-Forwarded-For` [http.middlewares] diff --git a/docs/content/middlewares/overview.md b/docs/content/middlewares/overview.md index a9825bec5..9af25e8fd 100644 --- a/docs/content/middlewares/overview.md +++ b/docs/content/middlewares/overview.md @@ -63,6 +63,13 @@ spec: - name: stripprefix ``` +```yaml tab="Consul Catalog" +# Create a middleware named `foo-add-prefix` +- "traefik.http.middlewares.foo-add-prefix.addprefix.prefix=/foo" +# Apply the middleware named `foo-add-prefix` to the router named `router1` +- "traefik.http.routers.router1.middlewares=foo-add-prefix@consulcatalog" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.foo-add-prefix.addprefix.prefix": "/foo", diff --git a/docs/content/middlewares/passtlsclientcert.md b/docs/content/middlewares/passtlsclientcert.md index 511adac35..6375da564 100644 --- a/docs/content/middlewares/passtlsclientcert.md +++ b/docs/content/middlewares/passtlsclientcert.md @@ -29,6 +29,11 @@ spec: pem: true ``` +```yaml tab="Consul Catalog" +# Pass the escaped pem in the `X-Forwarded-Tls-Client-Cert` header +- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.pem=true" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.pem": "true" @@ -111,26 +116,25 @@ http: domainComponent: true ``` - ```yaml tab="Rancher" + ```yaml tab="Consul Catalog" # Pass all the available info in the `X-Forwarded-Tls-Client-Cert-Info` header - labels: - - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.notafter=true" - - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.notbefore=true" - - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.sans=true" - - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.commonname=true" - - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.country=true" - - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.domaincomponent=true" - - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.locality=true" - - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organization=true" - - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.province=true" - - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.serialnumber=true" - - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.commonname=true" - - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.country=true" - - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.domaincomponent=true" - - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.locality=true" - - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.organization=true" - - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.province=true" - - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.serialnumber=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.notafter=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.notbefore=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.sans=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.commonname=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.country=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.domaincomponent=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.locality=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organization=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.province=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.serialnumber=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.commonname=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.country=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.domaincomponent=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.locality=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.organization=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.province=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.serialnumber=true" ``` ```json tab="Marathon" @@ -154,6 +158,28 @@ http: "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.serialnumber": "true" } ``` + + ```yaml tab="Rancher" + # Pass all the available info in the `X-Forwarded-Tls-Client-Cert-Info` header + labels: + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.notafter=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.notbefore=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.sans=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.commonname=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.country=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.domaincomponent=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.locality=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organization=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.province=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.serialnumber=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.commonname=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.country=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.domaincomponent=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.locality=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.organization=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.province=true" + - "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.serialnumber=true" + ``` ```toml tab="File (TOML)" # Pass all the available info in the `X-Forwarded-Tls-Client-Cert-Info` header diff --git a/docs/content/middlewares/ratelimit.md b/docs/content/middlewares/ratelimit.md index 2c3e74c36..7caf6f187 100644 --- a/docs/content/middlewares/ratelimit.md +++ b/docs/content/middlewares/ratelimit.md @@ -28,6 +28,13 @@ spec: burst: 50 ``` +```yaml tab="Consul Catalog" +# Here, an average of 100 requests per second is allowed. +# In addition, a burst of 50 requests is allowed. +- "traefik.http.middlewares.test-ratelimit.ratelimit.average=100" +- "traefik.http.middlewares.test-ratelimit.ratelimit.burst=50" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-ratelimit.ratelimit.average": "100", @@ -85,6 +92,10 @@ spec: average: 100 ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-ratelimit.ratelimit.average=100" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-ratelimit.ratelimit.average": "100", @@ -130,6 +141,10 @@ spec: burst: 100 ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-ratelimit.ratelimit.burst=100" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-ratelimit.ratelimit.burst": "100", @@ -138,8 +153,7 @@ spec: ```yaml tab="Rancher" labels: - - "traefik.http.middlewares.test-ratelimit.ratelimit.burst=100" - + - "traefik.http.middlewares.test-ratelimit.ratelimit.burst=100" ``` ```toml tab="File (TOML)" @@ -204,9 +218,8 @@ spec: - 192.168.1.7 ``` -```yaml tab="Rancher" -labels: - - "traefik.http.middlewares.test-ratelimit.ratelimit.sourcecriterion.ipstrategy.excludedips=127.0.0.1/32, 192.168.1.7" +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-ratelimit.ratelimit.sourcecriterion.ipstrategy.excludedips=127.0.0.1/32, 192.168.1.7" ``` ```json tab="Marathon" @@ -215,6 +228,11 @@ labels: } ``` +```yaml tab="Rancher" +labels: + - "traefik.http.middlewares.test-ratelimit.ratelimit.sourcecriterion.ipstrategy.excludedips=127.0.0.1/32, 192.168.1.7" +``` + ```toml tab="File (TOML)" [http.middlewares] [http.middlewares.test-ratelimit.rateLimit] @@ -268,9 +286,8 @@ spec: requestHeaderName: username ``` -```yaml tab="Rancher" -labels: - - "traefik.http.middlewares.test-ratelimit.ratelimit.sourcecriterion.requestheadername=username" +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-ratelimit.ratelimit.sourcecriterion.requestheadername=username" ``` ```json tab="Marathon" @@ -279,6 +296,11 @@ labels: } ``` +```yaml tab="Rancher" +labels: + - "traefik.http.middlewares.test-ratelimit.ratelimit.sourcecriterion.requestheadername=username" +``` + ```toml tab="File (TOML)" [http.middlewares] [http.middlewares.test-ratelimit.rateLimit] @@ -315,9 +337,8 @@ spec: requestHost: true ``` -```yaml tab="Rancher" -labels: - - "traefik.http.middlewares.test-ratelimit.ratelimit.sourcecriterion.requesthost=true" +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-ratelimit.ratelimit.sourcecriterion.requesthost=true" ``` ```json tab="Marathon" @@ -326,6 +347,11 @@ labels: } ``` +```yaml tab="Rancher" +labels: + - "traefik.http.middlewares.test-ratelimit.ratelimit.sourcecriterion.requesthost=true" +``` + ```toml tab="File (TOML)" [http.middlewares] [http.middlewares.test-ratelimit.rateLimit] diff --git a/docs/content/middlewares/redirectregex.md b/docs/content/middlewares/redirectregex.md index 9121fc55b..7152ebf02 100644 --- a/docs/content/middlewares/redirectregex.md +++ b/docs/content/middlewares/redirectregex.md @@ -31,6 +31,13 @@ spec: replacement: http://mydomain/${1} ``` +```yaml tab="Consul Catalog" +# Redirect with domain replacement +# Note: all dollar signs need to be doubled for escaping. +- "traefik.http.middlewares.test-redirectregex.redirectregex.regex=^http://localhost/(.*)" +- "traefik.http.middlewares.test-redirectregex.redirectregex.replacement=http://mydomain/$${1}" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-redirectregex.redirectregex.regex": "^http://localhost/(.*)", diff --git a/docs/content/middlewares/redirectscheme.md b/docs/content/middlewares/redirectscheme.md index fa0142dce..a67c2cf81 100644 --- a/docs/content/middlewares/redirectscheme.md +++ b/docs/content/middlewares/redirectscheme.md @@ -28,6 +28,12 @@ spec: scheme: https ``` +```yaml tab="Consul Catalog" +# Redirect to https +labels: +- "traefik.http.middlewares.test-redirectscheme.redirectscheme.scheme=https" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-redirectscheme.redirectscheme.scheme": "https" diff --git a/docs/content/middlewares/replacepath.md b/docs/content/middlewares/replacepath.md index 25e15cb68..51541b9cc 100644 --- a/docs/content/middlewares/replacepath.md +++ b/docs/content/middlewares/replacepath.md @@ -28,6 +28,11 @@ spec: path: /foo ``` +```yaml tab="Consul Catalog" +# Replace the path by /foo +- "traefik.http.middlewares.test-replacepath.replacepath.path=/foo" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-replacepath.replacepath.path": "/foo" diff --git a/docs/content/middlewares/replacepathregex.md b/docs/content/middlewares/replacepathregex.md index 9a88d976e..b1037a316 100644 --- a/docs/content/middlewares/replacepathregex.md +++ b/docs/content/middlewares/replacepathregex.md @@ -30,6 +30,12 @@ spec: replacement: /bar/$1 ``` +```yaml tab="Consul Catalog" +# Replace path with regex +- "traefik.http.middlewares.test-replacepathregex.replacepathregex.regex=^/foo/(.*)" +- "traefik.http.middlewares.test-replacepathregex.replacepathregex.replacement=/bar/$1" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-replacepathregex.replacepathregex.regex": "^/foo/(.*)", diff --git a/docs/content/middlewares/retry.md b/docs/content/middlewares/retry.md index 12c42fb44..31cc9728e 100644 --- a/docs/content/middlewares/retry.md +++ b/docs/content/middlewares/retry.md @@ -29,6 +29,11 @@ spec: attempts: 4 ``` +```yaml tab="Consul Catalog" +# Retry to send request 4 times +- "traefik.http.middlewares.test-retry.retry.attempts=4" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-retry.retry.attempts": "4" diff --git a/docs/content/middlewares/stripprefix.md b/docs/content/middlewares/stripprefix.md index f34ab014c..1390ae209 100644 --- a/docs/content/middlewares/stripprefix.md +++ b/docs/content/middlewares/stripprefix.md @@ -30,6 +30,11 @@ spec: - /fiibar ``` +```yaml tab="Consul Catalog" +# Strip prefix /foobar and /fiibar +- "traefik.http.middlewares.test-stripprefix.stripprefix.prefixes=/foobar,/fiibar" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-stripprefix.stripprefix.prefixes": "/foobar,/fiibar" diff --git a/docs/content/middlewares/stripprefixregex.md b/docs/content/middlewares/stripprefixregex.md index 7d45f1e1e..74b03d8b9 100644 --- a/docs/content/middlewares/stripprefixregex.md +++ b/docs/content/middlewares/stripprefixregex.md @@ -23,6 +23,10 @@ spec: - "/foo/[a-z0-9]+/[0-9]+/" ``` +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-stripprefixregex.stripprefixregex.regex=/foo/[a-z0-9]+/[0-9]+/" +``` + ```json tab="Marathon" "labels": { "traefik.http.middlewares.test-stripprefixregex.stripprefixregex.regex": "/foo/[a-z0-9]+/[0-9]+/" diff --git a/docs/content/operations/api.md b/docs/content/operations/api.md index 24911b440..51fc9859e 100644 --- a/docs/content/operations/api.md +++ b/docs/content/operations/api.md @@ -47,6 +47,14 @@ labels: - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" ``` +```yaml tab="Consul Catalog" +# Declaring the user list +- "traefik.http.routers.api.rule=PathPrefix(`/api`) || PathPrefix(`/dashboard`)" +- "traefik.http.routers.api.service=api@internal" +- "traefik.http.routers.api.middlewares=auth" +- "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" +``` + ```json tab="Marathon" "labels": { "traefik.http.routers.api.rule": "PathPrefix(`/api`) || PathPrefix(`/dashboard`)" diff --git a/docs/content/providers/consul-catalog.md b/docs/content/providers/consul-catalog.md new file mode 100644 index 000000000..d387f6ada --- /dev/null +++ b/docs/content/providers/consul-catalog.md @@ -0,0 +1,273 @@ +# Traefik & Consul Catalog + +A Story of Labels, Services & Containers +{: .subtitle } + +![Consul Catalog](../assets/img/providers/consul.png) + +Attach labels to your services and let Traefik do the rest! + +## Configuration Examples + +??? example "Configuring Consul Catalog & Deploying / Exposing Services" + + Enabling the consulcatalog provider + + ```toml tab="File (TOML)" + [providers.consulcatalog] + ``` + + ```yaml tab="File (YAML)" + providers: + consulcatalog: {} + ``` + + ```bash tab="CLI" + --providers.consulcatalog=true + ``` + + Attaching labels to services + + ```yaml + labels: + - traefik.http.services.my-service.rule=Host(`mydomain.com`) + ``` + +## Routing Configuration + +See the dedicated section in [routing](../routing/providers/consul-catalog.md). + +## Provider Configuration + +??? tip "Browse the Reference" + If you're in a hurry, maybe you'd rather go through the configuration reference: + + ```toml tab="File (TOML)" + --8<-- "content/providers/consul-catalog.toml" + ``` + + ```yaml tab="File (YAML)" + --8<-- "content/providers/consul-catalog.yml" + ``` + + ```bash tab="CLI" + --8<-- "content/providers/consul-catalog.txt" + ``` + +### `exposedByDefault` + +_Optional, Default=true_ + +```toml tab="File (TOML)" +[providers.consulcatalog] + exposedByDefault = false + # ... +``` + +```yaml tab="File (YAML)" +providers: + consulcatalog: + exposedByDefault: false + # ... +``` + +```bash tab="CLI" +--providers.consulcatalog.exposedByDefault=false +# ... +``` + +Expose Consul Catalog services by default in Traefik. +If set to false, services that don't have a `traefik.enable=true` label will be ignored from the resulting routing configuration. + +See also [Restrict the Scope of Service Discovery](./overview.md#restrict-the-scope-of-service-discovery). + +### `defaultRule` + +_Optional, Default=```Host(`{{ normalize .Name }}`)```_ + +```toml tab="File (TOML)" +[providers.consulcatalog] + defaultRule = "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)" + # ... +``` + +```yaml tab="File (YAML)" +providers: + consulcatalog: + defaultRule: "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)" + # ... +``` + +```bash tab="CLI" +--providers.consulcatalog.defaultRule="Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)" +# ... +``` + +The default host rule for all services. + +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 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.http.routers.Router1.rule` label. + +### `enableServiceHealthFilter` + +_Optional, Default=true_ + +```toml tab="File (TOML)" +[providers.consulcatalog] + enableServiceHealthFilter = false + # ... +``` + +```yaml tab="File (YAML)" +providers: + consulcatalog: + enableServiceHealthFilter: false + # ... +``` + +```bash tab="CLI" +--providers.consulcatalog.enableServiceHealthFilter=false +# ... +``` + +Filter services with unhealthy states and inactive states. + +### `refreshSeconds` + +_Optional, Default=15_ + +```toml tab="File (TOML)" +[providers.consulcatalog] + refreshSeconds = 30 + # ... +``` + +```yaml tab="File (YAML)" +providers: + consulcatalog: + refreshSeconds: 30 + # ... +``` + +```bash tab="CLI" +--providers.consulcatalog.refreshSeconds=30 +# ... +``` + +Defines the polling interval (in seconds). + +### `intervalPoll` + +_Optional, Default=false_ + +```toml tab="File (TOML)" +[providers.consulcatalog] + intervalPoll = true + # ... +``` + +```yaml tab="File (YAML)" +providers: + consulcatalog: + intervalPoll: true + # ... +``` + +```bash tab="CLI" +--providers.consulcatalog.intervalPoll=true +# ... +``` + +Poll the Consul Catalog metadata service for changes every `consulcatalog.refreshSeconds`, +which is less accurate than the default long polling technique which will provide near instantaneous updates to Traefik. + +### `prefix` + +_Optional, Default=/latest_ + +```toml tab="File (TOML)" +[providers.consulcatalog] + prefix = "/test" + # ... +``` + +```yaml tab="File (YAML)" +providers: + consulcatalog: + prefix: "/test" + # ... +``` + +```bash tab="CLI" +--providers.consulcatalog.prefix="/test" +# ... +``` + +Prefix used for accessing the Consul Catalog service + +### `constraints` + +_Optional, Default=""_ + +```toml tab="File (TOML)" +[providers.consulcatalog] + constraints = "Label(`a.label.name`, `foo`)" + # ... +``` + +```yaml tab="File (YAML)" +providers: + consulcatalog: + constraints: "Label(`a.label.name`, `foo`)" + # ... +``` + +```bash tab="CLI" +--providers.consulcatalog.constraints="Label(`a.label.name`, `foo`)" +# ... +``` + +Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container. +That is to say, if none of the container's labels match the expression, no route for the container is created. +If the expression is empty, all detected containers are included. + +The expression syntax is based on the `Label("key", "value")`, and `LabelRegex("key", "value")` functions, as well as the usual boolean logic, as shown in examples below. + +??? example "Constraints Expression Examples" + + ```toml + # Includes only containers having a label with key `a.label.name` and value `foo` + constraints = "Label(`a.label.name`, `foo`)" + ``` + + ```toml + # Excludes containers having any label with key `a.label.name` and value `foo` + constraints = "!Label(`a.label.name`, `value`)" + ``` + + ```toml + # With logical AND. + constraints = "Label(`a.label.name`, `valueA`) && Label(`another.label.name`, `valueB`)" + ``` + + ```toml + # With logical OR. + constraints = "Label(`a.label.name`, `valueA`) || Label(`another.label.name`, `valueB`)" + ``` + + ```toml + # With logical AND and OR, with precedence set by parentheses. + constraints = "Label(`a.label.name`, `valueA`) && (Label(`another.label.name`, `valueB`) || Label(`yet.another.label.name`, `valueC`))" + ``` + + ```toml + # Includes only containers having a label with key `a.label.name` and a value matching the `a.+` regular expression. + constraints = "LabelRegex(`a.label.name`, `a.+`)" + ``` + +See also [Restrict the Scope of Service Discovery](./overview.md#restrict-the-scope-of-service-discovery). diff --git a/docs/content/providers/consul-catalog.toml b/docs/content/providers/consul-catalog.toml new file mode 100644 index 000000000..7915ecfba --- /dev/null +++ b/docs/content/providers/consul-catalog.toml @@ -0,0 +1,44 @@ +# Enable Consul Catalog Provider. +[providers.consulcatalog] + + # Expose Consul Catalog services by default in Traefik. + exposedByDefault = true + + # Prefix used for accessing the Consul service metadata. + prefix = "traefik" + + # Defines the polling interval (in seconds). + refreshSeconds = 15 + + # Defines default rule. + defaultRule = "foobar" + + # Defines Consul Catalog Provider endpoint. + [providers.consulcatalog.endpoint] + + # Defines the consul address endpoint. + address = "127.0.0.1:8500" + + # Defines the scheme used. + scheme = "foobar" + + # Defines the DC. + datacenter = "foobar" + + # Defines the token. + token = "foobar" + + # Defines the expoint wait time. + endpointWaitTime = "15s" + + # Defines Consul Catalog Provider TLS endpoint. + [providers.consulcatalog.endpoint.tls] + + # Defines Consul Catalog Provider endpoint. + caOptional = true + + cert = "foobar" + + key = "foobar" + + insecureSkipVerify = true diff --git a/docs/content/providers/consul-catalog.txt b/docs/content/providers/consul-catalog.txt new file mode 100644 index 000000000..79781aae1 --- /dev/null +++ b/docs/content/providers/consul-catalog.txt @@ -0,0 +1,53 @@ +# Enable Consul Catalog Provider. +--providers.consulcatalog + +# Enable Consul Catalog Provider constraints. +--providers.consulcatalog.constraints + +# Defines the Consul Catalog default rule. +--providers.consulcatalog.defaultrule + +# Defines the Consul Catalog endpoint address. +--providers.consulcatalog.endpoint.address + +# Defines the Consul Catalog endpoint data-center. +--providers.consulcatalog.endpoint.datacenter + +# Defines the Consul Catalog endpoint wait time. +--providers.consulcatalog.endpoint.endpointwaittime + +# Defines the Consul Catalog endpoint http auth password. +--providers.consulcatalog.endpoint.httpauth.password + +# Defines the Consul Catalog endpoint http auth username. +--providers.consulcatalog.endpoint.httpauth.username + +# Defines the Consul Catalog endpoint scheme. +--providers.consulcatalog.endpoint.scheme + +# Defines the Consul Catalog endpoint tls CA. +--providers.consulcatalog.endpoint.tls.ca + +# Defines the Consul Catalog endpoint tls optional CA. +--providers.consulcatalog.endpoint.tls.caoptional + +# Defines the Consul Catalog endpoint tls cert. +--providers.consulcatalog.endpoint.tls.cert + +# Defines the Consul Catalog endpoint tls insecure skip verify. +--providers.consulcatalog.endpoint.tls.insecureskipverify + +# Defines the Consul Catalog endpoint tls key. +--providers.consulcatalog.endpoint.tls.key + +# Defines the Consul Catalog endpoint token. +--providers.consulcatalog.endpoint.token + +# Defines the Consul Catalog expose by default. +--providers.consulcatalog.exposedbydefault + +# Defines the Consul Catalog prefix. +--providers.consulcatalog.prefix + +# Defines the Consul Catalog refresh interval. +--providers.consulcatalog.refreshinterval diff --git a/docs/content/providers/consul-catalog.yml b/docs/content/providers/consul-catalog.yml new file mode 100644 index 000000000..dfa4f9954 --- /dev/null +++ b/docs/content/providers/consul-catalog.yml @@ -0,0 +1,28 @@ +# Enable Rancher Provider. +providers: + consulcatalog: + + # Defines the consul address endpoint. + address: 127.0.0.1:8500 + + # Defines the scheme used. + scheme: "foobar" + + # Defines the DC. + datacenter: "foobar" + + # Defines the token. + token: "foobar" + + # Defines the expoint wait time. + endpointWaitTime: "15s" + + # Defines Consul Catalog Provider TLS endpoint. + endpoint: + tls: + + # Defines Consul Catalog Provider endpoint. + caOptional: true + cert: "foobar" + key: "foobar" + insecureSkipVerify: true diff --git a/docs/content/reference/dynamic-configuration/consul-catalog.md b/docs/content/reference/dynamic-configuration/consul-catalog.md new file mode 100644 index 000000000..fd3a41de4 --- /dev/null +++ b/docs/content/reference/dynamic-configuration/consul-catalog.md @@ -0,0 +1,11 @@ +# Consul Catalog Configuration Reference + +Dynamic configuration with Consul Catalog +{: .subtitle } + +The labels are case insensitive. + +```yaml +--8<-- "content/reference/dynamic-configuration/consul-catalog.yml" +--8<-- "content/reference/dynamic-configuration/docker-labels.yml" +``` diff --git a/docs/content/reference/dynamic-configuration/consul-catalog.yml b/docs/content/reference/dynamic-configuration/consul-catalog.yml new file mode 100644 index 000000000..23efc00c6 --- /dev/null +++ b/docs/content/reference/dynamic-configuration/consul-catalog.yml @@ -0,0 +1 @@ +- "traefik.enable=true" diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md index e2b6322e2..31939b3bb 100644 --- a/docs/content/reference/static-configuration/cli-ref.md +++ b/docs/content/reference/static-configuration/cli-ref.md @@ -234,6 +234,57 @@ Enable ping. (Default: ```false```) `--ping.entrypoint`: EntryPoint (Default: ```traefik```) +`--providers.consulcatalog.constraints`: +Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container. + +`--providers.consulcatalog.defaultrule`: +Default rule. (Default: ```Host(`{{ normalize .Name }}`)```) + +`--providers.consulcatalog.endpoint.address`: +The address of the Consul server (Default: ```http://127.0.0.1:8500```) + +`--providers.consulcatalog.endpoint.datacenter`: +Data center to use. If not provided, the default agent data center is used + +`--providers.consulcatalog.endpoint.endpointwaittime`: +WaitTime limits how long a Watch will block. If not provided, the agent default values will be used (Default: ```0```) + +`--providers.consulcatalog.endpoint.httpauth.password`: +Basic Auth password + +`--providers.consulcatalog.endpoint.httpauth.username`: +Basic Auth username + +`--providers.consulcatalog.endpoint.scheme`: +The URI scheme for the Consul server + +`--providers.consulcatalog.endpoint.tls.ca`: +TLS CA + +`--providers.consulcatalog.endpoint.tls.caoptional`: +TLS CA.Optional (Default: ```false```) + +`--providers.consulcatalog.endpoint.tls.cert`: +TLS cert + +`--providers.consulcatalog.endpoint.tls.insecureskipverify`: +TLS insecure skip verify (Default: ```false```) + +`--providers.consulcatalog.endpoint.tls.key`: +TLS key + +`--providers.consulcatalog.endpoint.token`: +Token is used to provide a per-request ACL token which overrides the agent's default token + +`--providers.consulcatalog.exposedbydefault`: +Expose containers by default. (Default: ```true```) + +`--providers.consulcatalog.prefix`: +Prefix for consul service tags. Default 'traefik' (Default: ```traefik```) + +`--providers.consulcatalog.refreshinterval`: +Interval for check Consul API. Default 100ms (Default: ```15```) + `--providers.docker`: Enable Docker backend with default settings. (Default: ```false```) diff --git a/docs/content/reference/static-configuration/env-ref.md b/docs/content/reference/static-configuration/env-ref.md index 1e7a79b83..976c9db10 100644 --- a/docs/content/reference/static-configuration/env-ref.md +++ b/docs/content/reference/static-configuration/env-ref.md @@ -234,6 +234,57 @@ Enable ping. (Default: ```false```) `TRAEFIK_PING_ENTRYPOINT`: EntryPoint (Default: ```traefik```) +`TRAEFIK_PROVIDERS_CONSULCATALOG_CONSTRAINTS`: +Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container. + +`TRAEFIK_PROVIDERS_CONSULCATALOG_DEFAULTRULE`: +Default rule. (Default: ```Host(`{{ normalize .Name }}`)```) + +`TRAEFIK_PROVIDERS_CONSULCATALOG_ENDPOINT_ADDRESS`: +The address of the Consul server (Default: ```http://127.0.0.1:8500```) + +`TRAEFIK_PROVIDERS_CONSULCATALOG_ENDPOINT_DATACENTER`: +Data center to use. If not provided, the default agent data center is used + +`TRAEFIK_PROVIDERS_CONSULCATALOG_ENDPOINT_ENDPOINTWAITTIME`: +WaitTime limits how long a Watch will block. If not provided, the agent default values will be used (Default: ```0```) + +`TRAEFIK_PROVIDERS_CONSULCATALOG_ENDPOINT_HTTPAUTH_PASSWORD`: +Basic Auth password + +`TRAEFIK_PROVIDERS_CONSULCATALOG_ENDPOINT_HTTPAUTH_USERNAME`: +Basic Auth username + +`TRAEFIK_PROVIDERS_CONSULCATALOG_ENDPOINT_SCHEME`: +The URI scheme for the Consul server + +`TRAEFIK_PROVIDERS_CONSULCATALOG_ENDPOINT_TLS_CA`: +TLS CA + +`TRAEFIK_PROVIDERS_CONSULCATALOG_ENDPOINT_TLS_CAOPTIONAL`: +TLS CA.Optional (Default: ```false```) + +`TRAEFIK_PROVIDERS_CONSULCATALOG_ENDPOINT_TLS_CERT`: +TLS cert + +`TRAEFIK_PROVIDERS_CONSULCATALOG_ENDPOINT_TLS_INSECURESKIPVERIFY`: +TLS insecure skip verify (Default: ```false```) + +`TRAEFIK_PROVIDERS_CONSULCATALOG_ENDPOINT_TLS_KEY`: +TLS key + +`TRAEFIK_PROVIDERS_CONSULCATALOG_ENDPOINT_TOKEN`: +Token is used to provide a per-request ACL token which overrides the agent's default token + +`TRAEFIK_PROVIDERS_CONSULCATALOG_EXPOSEDBYDEFAULT`: +Expose containers by default. (Default: ```true```) + +`TRAEFIK_PROVIDERS_CONSULCATALOG_PREFIX`: +Prefix for consul service tags. Default 'traefik' (Default: ```traefik```) + +`TRAEFIK_PROVIDERS_CONSULCATALOG_REFRESHINTERVAL`: +Interval for check Consul API. Default 100ms (Default: ```15```) + `TRAEFIK_PROVIDERS_DOCKER`: Enable Docker backend with default settings. (Default: ```false```) diff --git a/docs/content/reference/static-configuration/file.toml b/docs/content/reference/static-configuration/file.toml index 3c314572c..4696c98f6 100644 --- a/docs/content/reference/static-configuration/file.toml +++ b/docs/content/reference/static-configuration/file.toml @@ -108,6 +108,24 @@ refreshSeconds = 42 intervalPoll = true prefix = "foobar" + [providers.consulcatalog] + constraints = "foobar" + prefix = "traefik" + defaultRule = "foobar" + exposedByDefault = true + refreshInterval = 15 + [providers.consulcatalog.endpoint] + address = "foobar" + scheme = "foobar" + datacenter = "foobar" + token = "foobar" + endpointWaitTime = "15s" + [providers.consulcatalog.endpoint.tls] + ca = "foobar" + caOptional = true + cert = "foobar" + key = "foobar" + insecureSkipVerify = true [api] insecure = true diff --git a/docs/content/reference/static-configuration/file.yaml b/docs/content/reference/static-configuration/file.yaml index e55eac104..114523f2b 100644 --- a/docs/content/reference/static-configuration/file.yaml +++ b/docs/content/reference/static-configuration/file.yaml @@ -115,6 +115,24 @@ providers: refreshSeconds: 42 intervalPoll: true prefix: foobar + consulcatalog: + constraints: foobar + prefix: traefik + defaultRule: foobar + exposedByDefault: true + refreshInterval: 15 + endpoint: + address: foobar + scheme: foobar + datacenter: foobar + token: foobar + endpointWaitTime: 15s + tls: + ca: foobar + caOptional: true + cert: foobar + key: foobar + insecureSkipVerify: true api: insecure: true dashboard: true diff --git a/docs/content/routing/providers/consul-catalog.md b/docs/content/routing/providers/consul-catalog.md new file mode 100644 index 000000000..bdbbe44cb --- /dev/null +++ b/docs/content/routing/providers/consul-catalog.md @@ -0,0 +1,388 @@ +# Traefik & Consul Catalog + +A Story of Labels, Services & Containers +{: .subtitle } + +![Rancher](../../assets/img/providers/consul.png) + +Attach labels to your services and let Traefik do the rest! + +## Routing Configuration + +!!! info "Labels" + + - Labels are case insensitive. + - The complete list of labels can be found [the reference page](../../reference/dynamic-configuration/consul-catalog.md) + +### General + +Traefik creates, for each consul Catalog service, a corresponding [service](../services/index.md) and [router](../routers/index.md). + +The Service automatically gets a server per container in this consul Catalog service, and the router gets a default rule attached to it, based on the service name. + +### Routers + +To update the configuration of the Router automatically attached to the container, add labels starting with `traefik.routers.{name-of-your-choice}.` and followed by the option you want to change. + +For example, to change the rule, you could add the label ```traefik.http.routers.my-container.rule=Host(`mydomain.com`)```. + +??? info "`traefik.http.routers..rule`" + + See [rule](../routers/index.md#rule) for more information. + + ```yaml + - "traefik.http.routers.myrouter.rule=Host(`mydomain.com`)" + ``` + +??? info "`traefik.http.routers..entrypoints`" + + See [entry points](../routers/index.md#entrypoints) for more information. + + ```yaml + - "traefik.http.routers.myrouter.entrypoints=web,websecure" + ``` + +??? info "`traefik.http.routers..middlewares`" + + See [middlewares](../routers/index.md#middlewares) and [middlewares overview](../../middlewares/overview.md) for more information. + + ```yaml + - "traefik.http.routers.myrouter.middlewares=auth,prefix,cb" + ``` + +??? info "`traefik.http.routers..service`" + + See [rule](../routers/index.md#service) for more information. + + ```yaml + - "traefik.http.routers.myrouter.service=myservice" + ``` + +??? info "`traefik.http.routers..tls`" + + See [tls](../routers/index.md#tls) for more information. + + ```yaml + - "traefik.http.routers.myrouter>.tls=true" + ``` + +??? info "`traefik.http.routers..tls.certresolver`" + + See [certResolver](../routers/index.md#certresolver) for more information. + + ```yaml + - "traefik.http.routers.myrouter.tls.certresolver=myresolver" + ``` + +??? info "`traefik.http.routers..tls.domains[n].main`" + + See [domains](../routers/index.md#domains) for more information. + + ```yaml + - "traefik.http.routers.myrouter.tls.domains[0].main=foobar.com" + ``` + +??? info "`traefik.http.routers..tls.domains[n].sans`" + + See [domains](../routers/index.md#domains) for more information. + + ```yaml + - "traefik.http.routers.myrouter.tls.domains[0].sans=test.foobar.com,dev.foobar.com" + ``` + +??? info "`traefik.http.routers..tls.options`" + + See [options](../routers/index.md#options) for more information. + + ```yaml + - "traefik.http.routers.myrouter.tls.options=foobar" + ``` + +??? info "`traefik.http.routers..priority`" + + + ```yaml + - "traefik.http.routers.myrouter.priority=42" + ``` + +### Services + +To update the configuration of the Service automatically attached to the container, +add labels starting with `traefik.http.services.{name-of-your-choice}.`, followed by the option you want to change. + +For example, to change the `passHostHeader` behavior, +you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.passhostheader=false`. + +??? info "`traefik.http.services..loadbalancer.server.port`" + + Registers a port. + Useful when the container exposes multiples ports. + + ```yaml + - "traefik.http.services.myservice.loadbalancer.server.port=8080" + ``` + +??? info "`traefik.http.services..loadbalancer.server.scheme`" + + Overrides the default scheme. + + ```yaml + - "traefik.http.services.myservice.loadbalancer.server.scheme=http" + ``` + +??? info "`traefik.http.services..loadbalancer.passhostheader`" + + + ```yaml + - "traefik.http.services.myservice.loadbalancer.passhostheader=true" + ``` + +??? info "`traefik.http.services..loadbalancer.healthcheck.headers.`" + + See [health check](../services/index.md#health-check) for more information. + + ```yaml + - "traefik.http.services.myservice.loadbalancer.healthcheck.headers.X-Foo=foobar" + ``` + +??? info "`traefik.http.services..loadbalancer.healthcheck.hostname`" + + See [health check](../services/index.md#health-check) for more information. + + ```yaml + - "traefik.http.services.myservice.loadbalancer.healthcheck.hostname=foobar.com" + ``` + +??? info "`traefik.http.services..loadbalancer.healthcheck.interval`" + + See [health check](../services/index.md#health-check) for more information. + + ```yaml + - "traefik.http.services.myservice.loadbalancer.healthcheck.interval=10" + ``` + +??? info "`traefik.http.services..loadbalancer.healthcheck.path`" + + See [health check](../services/index.md#health-check) for more information. + + ```yaml + - "traefik.http.services.myservice.loadbalancer.healthcheck.path=/foo" + ``` + +??? info "`traefik.http.services..loadbalancer.healthcheck.port`" + + See [health check](../services/index.md#health-check) for more information. + + ```yaml + - "traefik.http.services.myservice.loadbalancer.healthcheck.port=42" + ``` + +??? info "`traefik.http.services..loadbalancer.healthcheck.scheme`" + + See [health check](../services/index.md#health-check) for more information. + + ```yaml + - "traefik.http.services.myservice.loadbalancer.healthcheck.scheme=http" + ``` + +??? info "`traefik.http.services..loadbalancer.healthcheck.timeout`" + + See [health check](../services/index.md#health-check) for more information. + + ```yaml + - "traefik.http.services.myservice.loadbalancer.healthcheck.timeout=10" + ``` + +??? info "`traefik.http.services..loadbalancer.sticky`" + + See [sticky sessions](../services/index.md#sticky-sessions) for more information. + + ```yaml + - "traefik.http.services.myservice.loadbalancer.sticky=true" + ``` + +??? info "`traefik.http.services..loadbalancer.sticky.cookie.httponly`" + + See [sticky sessions](../services/index.md#sticky-sessions) for more information. + + ```yaml + - "traefik.http.services.myservice.loadbalancer.sticky.cookie.httponly=true" + ``` + +??? info "`traefik.http.services..loadbalancer.sticky.cookie.name`" + + See [sticky sessions](../services/index.md#sticky-sessions) for more information. + + ```yaml + - "traefik.http.services.myservice.loadbalancer.sticky.cookie.name=foobar" + ``` + +??? info "`traefik.http.services..loadbalancer.sticky.cookie.secure`" + + See [sticky sessions](../services/index.md#sticky-sessions) for more information. + + ```yaml + - "traefik.http.services.myservice.loadbalancer.sticky.cookie.secure=true" + ``` + +??? info "`traefik.http.services..loadbalancer.responseforwarding.flushinterval`" + + + FlushInterval specifies the flush interval to flush to the client while copying the response body. + + ```yaml + - "traefik.http.services.myservice.loadbalancer.responseforwarding.flushinterval=10" + ``` + +### 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 [`redirectscheme`](../../middlewares/redirectscheme.md) named `my-redirect`, you'd write `traefik.http.middlewares.my-redirect.redirectscheme.scheme: https`. + +More information about available middlewares in the dedicated [middlewares section](../../middlewares/overview.md). + +??? example "Declaring and Referencing a Middleware" + + ```yaml + # ... + labels: + # Declaring a middleware + - traefik.http.middlewares.my-redirect.redirectscheme.scheme=https + # Referencing a middleware + - traefik.http.routers.my-container.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" + + ```yaml + services: + my-container: + # ... + 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 (like it does by default if no TCP Router/Service is defined). + You can declare both a TCP Router/Service and an HTTP Router/Service for the same container (but you have to do so manually). + +#### TCP Routers + +??? info "`traefik.tcp.routers..entrypoints`" + + See [entry points](../routers/index.md#entrypoints_1) for more information. + + ```yaml + - "traefik.tcp.routers.mytcprouter.entrypoints=ep1,ep2" + ``` + +??? info "`traefik.tcp.routers..rule`" + + See [rule](../routers/index.md#rule_1) for more information. + + ```yaml + - "traefik.tcp.routers.mytcprouter.rule=HostSNI(`myhost.com`)" + ``` + +??? info "`traefik.tcp.routers..service`" + + See [service](../routers/index.md#services) for more information. + + ```yaml + - "traefik.tcp.routers.mytcprouter.service=myservice" + ``` + +??? info "`traefik.tcp.routers..tls`" + + See [TLS](../routers/index.md#tls_1) for more information. + + ```yaml + - "traefik.tcp.routers.mytcprouter.tls=true" + ``` + +??? info "`traefik.tcp.routers..tls.certresolver`" + + See [certResolver](../routers/index.md#certresolver_1) for more information. + + ```yaml + - "traefik.tcp.routers.mytcprouter.tls.certresolver=myresolver" + ``` + +??? info "`traefik.tcp.routers..tls.domains[n].main`" + + See [domains](../routers/index.md#domains_1) for more information. + + ```yaml + - "traefik.tcp.routers.mytcprouter.tls.domains[0].main=foobar.com" + ``` + +??? info "`traefik.tcp.routers..tls.domains[n].sans`" + + See [domains](../routers/index.md#domains_1) for more information. + + ```yaml + - "traefik.tcp.routers.mytcprouter.tls.domains[0].sans=test.foobar.com,dev.foobar.com" + ``` + +??? info "`traefik.tcp.routers..tls.options`" + + See [options](../routers/index.md#options_1) for more information. + + ```yaml + - "traefik.tcp.routers.mytcprouter.tls.options=mysoptions" + ``` + +??? info "`traefik.tcp.routers..tls.passthrough`" + + See [TLS](../routers/index.md#tls_1) for more information. + + ```yaml + - "traefik.tcp.routers.mytcprouter.tls.passthrough=true" + ``` + +#### TCP Services + +??? info "`traefik.tcp.services..loadbalancer.server.port`" + + Registers a port of the application. + + ```yaml + - "traefik.tcp.services.mytcpservice.loadbalancer.server.port=423" + ``` + +??? info "`traefik.tcp.services..loadbalancer.terminationdelay`" + + See [termination delay](../services/index.md#termination-delay) for more information. + + ```yaml + - "traefik.tcp.services.mytcpservice.loadbalancer.terminationdelay=100" + ``` + +### Specific Provider Options + +#### `traefik.enable` + +```yaml +- "traefik.enable=true" +``` + +You can tell Traefik to consider (or not) the container by setting `traefik.enable` to true or false. + +This option overrides the value of `exposedByDefault`. + +#### Port Lookup + +Traefik is capable of detecting the port to use, by following the default consul Catalog flow. +That means, if you just expose lets say port `:1337` on the consul Catalog ui, traefik will pick up this port and use it. diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 3a111788c..bda577abe 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -77,9 +77,10 @@ nav: - 'Docker': 'providers/docker.md' - 'Kubernetes IngressRoute': 'providers/kubernetes-crd.md' - 'Kubernetes Ingress': 'providers/kubernetes-ingress.md' + - 'Consul Catalog': 'providers/consul-catalog.md' + - 'Marathon': 'providers/marathon.md' - 'Rancher': 'providers/rancher.md' - 'File': 'providers/file.md' - - 'Marathon': 'providers/marathon.md' - 'Routing & Load Balancing': - 'Overview': 'routing/overview.md' - 'EntryPoints': 'routing/entrypoints.md' @@ -88,8 +89,9 @@ nav: - 'Providers': - 'Docker': 'routing/providers/docker.md' - 'Kubernetes IngressRoute': 'routing/providers/kubernetes-crd.md' - - 'Rancher': 'routing/providers/rancher.md' + - 'Consul Catalog': 'routing/providers/consul-catalog.md' - 'Marathon': 'routing/providers/marathon.md' + - 'Rancher': 'routing/providers/rancher.md' - 'HTTPS & TLS': - 'Overview': 'https/overview.md' - 'TLS': 'https/tls.md' @@ -170,5 +172,6 @@ nav: - 'File': 'reference/dynamic-configuration/file.md' - 'Docker': 'reference/dynamic-configuration/docker.md' - 'Kubernetes CRD': 'reference/dynamic-configuration/kubernetes-crd.md' + - 'Consul Catalog': 'reference/dynamic-configuration/consul-catalog.md' - 'Marathon': 'reference/dynamic-configuration/marathon.md' - 'Rancher': 'reference/dynamic-configuration/rancher.md' diff --git a/go.mod b/go.mod index bbf825fb4..08214219d 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/Shopify/sarama v1.23.1 // indirect github.com/VividCortex/gohistogram v1.0.0 // indirect github.com/abbot/go-http-auth v0.0.0-00010101000000-000000000000 - github.com/abronan/valkeyrie v0.0.0-20190802193736-ed4c4a229894 + github.com/abronan/valkeyrie v0.0.0-20190822142731-f2e1850dc905 github.com/c0va23/go-proxyprotocol v0.9.1 github.com/cenkalti/backoff/v3 v3.0.0 github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc // indirect @@ -47,6 +47,7 @@ require ( github.com/googleapis/gnostic v0.1.0 // indirect github.com/gorilla/mux v1.7.3 github.com/gorilla/websocket v1.4.0 + github.com/hashicorp/consul/api v1.2.0 github.com/hashicorp/go-version v1.2.0 github.com/huandu/xstrings v1.2.0 // indirect github.com/influxdata/influxdb1-client v0.0.0-20190402204710-8ff2fc3824fc @@ -72,7 +73,7 @@ require ( github.com/philhofer/fwd v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 github.com/prometheus/client_golang v1.1.0 - github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 + github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 github.com/rancher/go-rancher-metadata v0.0.0-00010101000000-000000000000 github.com/sirupsen/logrus v1.4.2 github.com/stretchr/testify v1.4.0 diff --git a/go.sum b/go.sum index 874f49b46..dad56baaf 100644 --- a/go.sum +++ b/go.sum @@ -35,6 +35,7 @@ github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvd github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.3.6-0.20190409195224-796139022798 h1:2T/jmrHeTezcCM58lvEQXs0UpQJCo5SoGAcg+mbSTIg= github.com/DataDog/zstd v1.3.6-0.20190409195224-796139022798/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/ExpediaDotCom/haystack-client-go v0.0.0-20190315171017-e7edbdf53a61 h1:1NIUJ+MAMpqDr4LWIfNsoJR+G7zg/8GZVwuRkmJxtTc= @@ -60,8 +61,8 @@ github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWso github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/abronan/valkeyrie v0.0.0-20190802193736-ed4c4a229894 h1:6oe+/ZnkM+gEJL+28sgHiCvO6qt15NE2lm52BuzBees= -github.com/abronan/valkeyrie v0.0.0-20190802193736-ed4c4a229894/go.mod h1:sQZ/48uDt1GRBDNsLboJGPD2w/HxEOhqf3JiikfHj1I= +github.com/abronan/valkeyrie v0.0.0-20190822142731-f2e1850dc905 h1:JG0OqQLCILn6ywoXJncu+/MFTTapP3aIIDDqB593HMc= +github.com/abronan/valkeyrie v0.0.0-20190822142731-f2e1850dc905/go.mod h1:hTreU6x9m2IP2h8e0TGrSzAXSCI3lxic8/JT5CMknjY= github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.0 h1:rXPPPxDA4GCPN0YWwyVHMzcxVpVg8gai2uGhJ3VqOSs= github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.0/go.mod h1:zpDJeKyp9ScW4NNrbdr+Eyxvry3ilGPewKoXw3XGN1k= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -71,6 +72,11 @@ github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190808125512-07798873deee/go.mod github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/apache/thrift v0.12.0 h1:pODnxUFNcjP9UTLZGTdeh+j16A8lJbRvD3rOtrk/7bs= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 h1:EFSB7Zo9Eg91v7MJPVsifUysc/wPdN+NOnVe6bWbdBM= +github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go v1.16.23/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.23.0 h1:ilfJN/vJtFo1XDFxB2YMBYGeOvGZl6Qow17oyD4+Z9A= github.com/aws/aws-sdk-go v1.23.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= @@ -80,12 +86,15 @@ github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/c0va23/go-proxyprotocol v0.9.1 h1:5BCkp0fDJOhzzH1lhjUgHhmZz9VvRMMif1U2D31hb34= github.com/c0va23/go-proxyprotocol v0.9.1/go.mod h1:TNjUV+llvk8TvWJxlPYAeAYZgSzT/iicNr3nWBWX320= github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXGM30YZL1WW/M337pXml+GrcZ4= github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/cloudflare-go v0.10.2 h1:VBodKICVPnwmDxstcW3biKcDSpFIfS/RELUXsZSBYK4= github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY= @@ -109,10 +118,12 @@ github.com/containous/multibuf v0.0.0-20190809014333-8b6c9a7e6bba h1:PhR03pep+5e github.com/containous/multibuf v0.0.0-20190809014333-8b6c9a7e6bba/go.mod h1:zkWcASFUJEst6QwCrxLdkuw1gvaKqmflEipm+iecV5M= github.com/containous/mux v0.0.0-20181024131434-c33f32e26898 h1:1srn9voikJGofblBhWy3WuZWqo14Ou7NaswNG/I2yWc= github.com/containous/mux v0.0.0-20181024131434-c33f32e26898/go.mod h1:z8WW7n06n8/1xF9Jl9WmuDeZuHAhfL+bwarNjsciwwg= +github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpu/goacmedns v0.0.1 h1:GeIU5chKys9zmHgOAgP+bstRaLqcGQ6HJh/hLw9hrus= github.com/cpu/goacmedns v0.0.1/go.mod h1:sesf/pNnCYwUevQEQfEwY0Y3DydlQWSGZbaMElOWxok= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= @@ -170,6 +181,7 @@ github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5I github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/exoscale/egoscale v0.18.1 h1:1FNZVk8jHUx0AvWhOZxLEDNlacTU0chMXUUNkm9EZaI= github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.0 h1:gh8fMGz0rlOv/1WmRZm7OgncIOTsAj21iNJot48omJQ= @@ -208,6 +220,8 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekf github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -221,6 +235,8 @@ github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -255,22 +271,56 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA github.com/gravitational/trace v0.0.0-20190726142706-a535a178675f h1:68WxnfBzJRYktZ30fmIjGQ74RsXYLoeH2/NITPktTMY= github.com/gravitational/trace v0.0.0-20190726142706-a535a178675f/go.mod h1:RvdOUHE4SHqR3oXlFFKnGzms8a5dugHygGw1bqDstYI= github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE= github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= -github.com/hashicorp/consul v1.4.0/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/api v1.2.0 h1:oPsuzLp2uk7I7rojPKuncWbZ+m5TMoD4Ivs+2Rkeh4Y= +github.com/hashicorp/consul/api v1.2.0/go.mod h1:1SIkFYi2ZTXUE5Kgt179+4hH33djo11+0Eo2XgTAtkw= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.2.0 h1:GWFYFmry/k4b1hEoy7kSkmU8e30GAyI4VZHk0fRxeL4= +github.com/hashicorp/consul/sdk v0.2.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= +github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/serf v0.8.1/go.mod h1:h/Ru6tmZazX7WO/GDmwdpS975F019L4t5ng5IgwbNrE= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/memberlist v0.1.4 h1:gkyML/r71w3FL8gUi74Vk76avkj/9lYAY9lvg0OcoGs= +github.com/hashicorp/memberlist v0.1.4/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0= @@ -335,22 +385,30 @@ github.com/mailgun/timetools v0.0.0-20141028012446-7e6055773c51 h1:Kg/NPZLLC3aAF github.com/mailgun/timetools v0.0.0-20141028012446-7e6055773c51/go.mod h1:RYmqHbhWwIz3z9eVmQ2rx82rulEMG0t+Q1bzfc9DYN4= github.com/mailgun/ttlmap v0.0.0-20170619185759-c1c17f74874f h1:ZZYhg16XocqSKPGNQAe0aeweNtFxuedbwwb4fSlg7h4= github.com/mailgun/ttlmap v0.0.0-20170619185759-c1c17f74874f/go.mod h1:8heskWJ5c0v5J9WH89ADhyal1DOZcayll8fSbhB+/9A= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.15 h1:CSSIDtllwGLMoA6zjdKnaE6Tx6eVUxQ29LUgGetiDCI= github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y= github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= @@ -405,6 +463,9 @@ github.com/oracle/oci-go-sdk v7.0.0+incompatible h1:oj5ESjXwwkFRdhZSnPlShvLWYdt/ github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014 h1:37VE5TYj2m/FLA9SNr4z0+A0JefvTmR60Zwf8XSEV7c= github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= @@ -420,7 +481,9 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= @@ -430,11 +493,15 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1: github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE= @@ -445,10 +512,13 @@ github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqn github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sacloud/libsacloud v1.26.1 h1:td3Kd7lvpSAxxHEVpnaZ9goHmmhi0D/RfP0Rqqf/kek= github.com/sacloud/libsacloud v1.26.1/go.mod h1:79ZwATmHLIFZIMd7sxA3LwzVy/B77uj3LDoToVTxDoQ= github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= @@ -459,6 +529,7 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykE github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -476,16 +547,17 @@ github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7 h1:CpHxIaZzVy26G github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7/go.mod h1:imsgLplxEC/etjIhdr3dNzV3JeT27LbVu5pYWm0JCBY= github.com/tinylib/msgp v1.0.2 h1:DfdQrzQa7Yh2es9SuLkixqxuXS2SxsdYn0KbdrOGWD8= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/transip/gotransip v0.0.0-20190812104329-6d8d9179b66f/go.mod h1:i0f4R4o2HM0m3DZYQWsj6/MEowD57VzoH0v3d7igeFY= github.com/transip/gotransip v5.8.2+incompatible h1:aNJhw/w/3QBqFcHAIPz1ytoK5FexeMzbUCGrrhWr3H0= github.com/transip/gotransip v5.8.2+incompatible/go.mod h1:uacMoJVmrfOcscM4Bi5NVg708b7c6rz2oDTWqa7i2Ic= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/uber-go/atomic v1.3.2 h1:Azu9lPBWRNKzYXSIwRfgRuDuS0YKsK4NFhiQv98gkxo= github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= github.com/uber/jaeger-client-go v2.19.0+incompatible h1:pbwbYfHUoaase0oPQOdZ1GcaUjImYGimUXSQ/+8+Z8Q= github.com/uber/jaeger-client-go v2.19.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/GfSYVCjK7dyaw= github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= -github.com/ugorji/go v0.0.0-20171019201919-bdcc60b419d1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= github.com/unrolled/render v1.0.1 h1:VDDnQQVfBMsOsp3VaCJszSO0nkBIVEYoPWeRThk9spY= github.com/unrolled/render v1.0.1/go.mod h1:gN9T0NhL4Bfbwu8ann7Ry/TGHYfosul+J0obPf6NBdM= github.com/unrolled/secure v1.0.4 h1:DksfKsRTyXP2R8quDdOOuRpRO45VprFL0X9t9+JX1PU= @@ -507,6 +579,7 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.1.0 h1:ngVtJC9TY/lg0AA/1k48FYhBrhRoFlEmWzsehpNAaZg= github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= go.etcd.io/bbolt v1.3.1-etcd.8/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v3.3.13+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -515,11 +588,16 @@ go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277 h1:d9qaMM+ODpCq+9We41//fu/sHsTnXcrqd1en3x+GKy4= go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= @@ -541,7 +619,9 @@ golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -567,9 +647,11 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -629,7 +711,6 @@ google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= diff --git a/integration/consul_catalog_test.go b/integration/consul_catalog_test.go new file mode 100644 index 000000000..86c69e03c --- /dev/null +++ b/integration/consul_catalog_test.go @@ -0,0 +1,342 @@ +package integration + +import ( + "fmt" + "net/http" + "os" + "strconv" + "time" + + "github.com/containous/traefik/v2/integration/try" + "github.com/docker/docker/integration-cli/checker" + "github.com/go-check/check" + "github.com/hashicorp/consul/api" +) + +type ConsulCatalogSuite struct { + BaseSuite + consulClient *api.Client + consulAddress string +} + +func (s *ConsulCatalogSuite) SetUpSuite(c *check.C) { + s.createComposeProject(c, "consul_catalog") + s.composeProject.Start(c) + s.consulAddress = "http://" + s.composeProject.Container(c, "consul").NetworkSettings.IPAddress + ":8500" + client, err := api.NewClient(&api.Config{ + Address: s.consulAddress, + }) + c.Check(err, check.IsNil) + s.consulClient = client + + // Wait for consul to elect itself leader + err = s.waitToElectConsulLeader() + c.Assert(err, checker.IsNil) +} + +func (s *ConsulCatalogSuite) waitToElectConsulLeader() error { + return try.Do(15*time.Second, func() error { + leader, err := s.consulClient.Status().Leader() + + if err != nil || len(leader) == 0 { + return fmt.Errorf("leader not found. %v", err) + } + + return nil + }) +} + +func (s *ConsulCatalogSuite) TearDownSuite(c *check.C) { + // shutdown and delete compose project + if s.composeProject != nil { + s.composeProject.Stop(c) + } +} + +func (s *ConsulCatalogSuite) registerService(id, name, address, port string, tags []string) error { + iPort, err := strconv.Atoi(port) + if err != nil { + return err + } + + return s.consulClient.Agent().ServiceRegister(&api.AgentServiceRegistration{ + ID: id, + Name: name, + Address: address, + Port: iPort, + Tags: tags, + }) +} + +func (s *ConsulCatalogSuite) deregisterService(id string) error { + return s.consulClient.Agent().ServiceDeregister(id) +} + +func (s *ConsulCatalogSuite) TestWithNotExposedByDefaultAndDefaultsSettings(c *check.C) { + err := s.registerService("whoami1", "whoami", s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress, "80", []string{"traefik.enable=true"}) + c.Assert(err, checker.IsNil) + err = s.registerService("whoami2", "whoami", s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress, "80", []string{"traefik.enable=true"}) + c.Assert(err, checker.IsNil) + err = s.registerService("whoami3", "whoami", s.composeProject.Container(c, "whoami3").NetworkSettings.IPAddress, "80", []string{"traefik.enable=true"}) + c.Assert(err, checker.IsNil) + + tempObjects := struct { + ConsulAddress string + }{ + ConsulAddress: s.consulAddress, + } + + file := s.adaptFile(c, "fixtures/consul_catalog/default_not_exposed.toml", tempObjects) + defer os.Remove(file) + + cmd, display := s.traefikCmd(withConfigFile(file)) + defer display(c) + err = cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil) + c.Assert(err, checker.IsNil) + req.Host = "whoami" + + err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami1", "Hostname: whoami2", "Hostname: whoami3")) + c.Assert(err, checker.IsNil) + + err = s.deregisterService("whoami1") + c.Assert(err, checker.IsNil) + err = s.deregisterService("whoami2") + c.Assert(err, checker.IsNil) + err = s.deregisterService("whoami3") + c.Assert(err, checker.IsNil) +} + +func (s *ConsulCatalogSuite) TestByLabels(c *check.C) { + labels := []string{ + "traefik.enable=true", + "traefik.http.routers.router1.rule=Path(`/whoami`)", + "traefik.http.routers.router1.service=service1", + "traefik.http.services.service1.loadBalancer.server.url=http://" + s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress, + } + + err := s.registerService("whoami1", "whoami", s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress, "80", labels) + c.Assert(err, checker.IsNil) + + tempObjects := struct { + ConsulAddress string + }{ + ConsulAddress: s.consulAddress, + } + + file := s.adaptFile(c, "fixtures/consul_catalog/default_not_exposed.toml", tempObjects) + defer os.Remove(file) + + cmd, display := s.traefikCmd(withConfigFile(file)) + defer display(c) + err = cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + err = try.GetRequest("http://127.0.0.1:8000/whoami", 2*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContainsOr("Hostname: whoami1", "Hostname: whoami2", "Hostname: whoami3")) + c.Assert(err, checker.IsNil) + + err = s.deregisterService("whoami1") + c.Assert(err, checker.IsNil) +} + +func (s *ConsulCatalogSuite) TestSimpleConfiguration(c *check.C) { + tempObjects := struct { + ConsulAddress string + DefaultRule string + }{ + ConsulAddress: s.consulAddress, + DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)", + } + + file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects) + defer os.Remove(file) + + err := s.registerService("whoami1", "whoami", s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress, "80", []string{"traefik.enable=true"}) + c.Assert(err, checker.IsNil) + + cmd, display := s.traefikCmd(withConfigFile(file)) + defer display(c) + err = cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil) + c.Assert(err, checker.IsNil) + req.Host = "whoami.consul.localhost" + + err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami1")) + c.Assert(err, checker.IsNil) + + err = s.deregisterService("whoami1") + c.Assert(err, checker.IsNil) +} + +func (s *ConsulCatalogSuite) TestDefaultConsulService(c *check.C) { + tempObjects := struct { + ConsulAddress string + DefaultRule string + }{ + + ConsulAddress: s.consulAddress, + DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)", + } + + file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects) + defer os.Remove(file) + + err := s.registerService("whoami1", "whoami", s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress, "80", nil) + c.Assert(err, checker.IsNil) + + // Start traefik + cmd, display := s.traefikCmd(withConfigFile(file)) + defer display(c) + err = cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil) + c.Assert(err, checker.IsNil) + req.Host = "whoami.consul.localhost" + + err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami1")) + c.Assert(err, checker.IsNil) + + err = s.deregisterService("whoami1") + c.Assert(err, checker.IsNil) +} + +func (s *ConsulCatalogSuite) TestConsulServiceWithTCPLabels(c *check.C) { + tempObjects := struct { + ConsulAddress string + DefaultRule string + }{ + ConsulAddress: s.consulAddress, + DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)", + } + + file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects) + defer os.Remove(file) + + // Start a container with some labels + labels := []string{ + "traefik.tcp.Routers.Super.Rule=HostSNI(`my.super.host`)", + "traefik.tcp.Routers.Super.tls=true", + "traefik.tcp.Services.Super.Loadbalancer.server.port=8080", + } + + err := s.registerService("whoamitcp", "whoamitcp", s.composeProject.Container(c, "whoamitcp").NetworkSettings.IPAddress, "8080", labels) + c.Assert(err, checker.IsNil) + + // Start traefik + cmd, display := s.traefikCmd(withConfigFile(file)) + defer display(c) + err = cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI(`my.super.host`)")) + c.Assert(err, checker.IsNil) + + who, err := guessWho("127.0.0.1:8000", "my.super.host", true) + c.Assert(err, checker.IsNil) + + c.Assert(who, checker.Contains, "whoamitcp") + + err = s.deregisterService("whoamitcp") + c.Assert(err, checker.IsNil) +} + +func (s *ConsulCatalogSuite) TestConsulServiceWithLabels(c *check.C) { + tempObjects := struct { + ConsulAddress string + DefaultRule string + }{ + ConsulAddress: s.consulAddress, + DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)", + } + + file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects) + defer os.Remove(file) + + // Start a container with some labels + labels := []string{ + "traefik.http.Routers.Super.Rule=Host(`my.super.host`)", + } + err := s.registerService("whoami1", "whoami", s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress, "80", labels) + c.Assert(err, checker.IsNil) + + // Start another container by replacing a '.' by a '-' + labels = []string{ + "traefik.http.Routers.SuperHost.Rule=Host(`my-super.host`)", + } + err = s.registerService("whoami2", "whoami", s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress, "80", labels) + c.Assert(err, checker.IsNil) + + // Start traefik + cmd, display := s.traefikCmd(withConfigFile(file)) + defer display(c) + err = cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil) + c.Assert(err, checker.IsNil) + req.Host = "my-super.host" + + err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami1")) + c.Assert(err, checker.IsNil) + + req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil) + c.Assert(err, checker.IsNil) + req.Host = "my.super.host" + + err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami2")) + c.Assert(err, checker.IsNil) + + err = s.deregisterService("whoami1") + c.Assert(err, checker.IsNil) + + err = s.deregisterService("whoami2") + c.Assert(err, checker.IsNil) +} + +func (s *ConsulCatalogSuite) TestConsulServiceWithOneMissingLabels(c *check.C) { + tempObjects := struct { + ConsulAddress string + DefaultRule string + }{ + ConsulAddress: s.consulAddress, + DefaultRule: "Host(`{{ normalize .Name }}.consul.localhost`)", + } + + file := s.adaptFile(c, "fixtures/consul_catalog/simple.toml", tempObjects) + defer os.Remove(file) + + // Start a container with some labels + labels := []string{ + "traefik.random.value=my.super.host", + } + err := s.registerService("whoami1", "whoami", s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress, "80", labels) + c.Assert(err, checker.IsNil) + + // Start traefik + cmd, display := s.traefikCmd(withConfigFile(file)) + defer display(c) + err = cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/version", nil) + c.Assert(err, checker.IsNil) + req.Host = "my.super.host" + + // FIXME Need to wait than 500 milliseconds more (for swarm or traefik to boot up ?) + // TODO validate : run on 80 + // Expected a 404 as we did not configure anything + err = try.Request(req, 1500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound)) + c.Assert(err, checker.IsNil) +} diff --git a/integration/fixtures/consul_catalog/default_not_exposed.toml b/integration/fixtures/consul_catalog/default_not_exposed.toml new file mode 100644 index 000000000..49e2bebc9 --- /dev/null +++ b/integration/fixtures/consul_catalog/default_not_exposed.toml @@ -0,0 +1,20 @@ +[global] + checkNewVersion = false + sendAnonymousUsage = false + +[log] + level = "DEBUG" + +[entryPoints] + [entryPoints.web] + address = ":8000" + +[api] + insecure = true + +[providers] + [providers.consulcatalog] + exposedByDefault = false + refreshInterval = "500ms" + [providers.consulcatalog.endpoint] + address = "{{ .ConsulAddress }}" diff --git a/integration/fixtures/consul_catalog/simple.toml b/integration/fixtures/consul_catalog/simple.toml new file mode 100644 index 000000000..59506bb1d --- /dev/null +++ b/integration/fixtures/consul_catalog/simple.toml @@ -0,0 +1,21 @@ +[global] + checkNewVersion = false + sendAnonymousUsage = false + +[log] + level = "DEBUG" + +[entryPoints] + [entryPoints.web] + address = ":8000" + +[api] + insecure = true + +[providers] + [providers.consulcatalog] + exposedByDefault = true + refreshInterval = "500ms" + defaultRule = "{{ .DefaultRule }}" + [providers.consulcatalog.endpoint] + address = "{{ .ConsulAddress }}" diff --git a/integration/integration_test.go b/integration/integration_test.go index daca739d5..130eaeee3 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -36,6 +36,7 @@ func Test(t *testing.T) { // tests launched from a container check.Suite(&AccessLogSuite{}) check.Suite(&AcmeSuite{}) + check.Suite(&ConsulCatalogSuite{}) check.Suite(&DockerComposeSuite{}) check.Suite(&DockerSuite{}) check.Suite(&ErrorPagesSuite{}) diff --git a/integration/resources/compose/consul_catalog.yml b/integration/resources/compose/consul_catalog.yml new file mode 100644 index 000000000..9299d95f3 --- /dev/null +++ b/integration/resources/compose/consul_catalog.yml @@ -0,0 +1,16 @@ +consul: + image: consul:1.6.1 + ports: + - 8500:8500 +whoami1: + image: containous/whoami:v1.3.0 + hostname: whoami1 +whoami2: + image: containous/whoami:v1.3.0 + hostname: whoami2 +whoami3: + image: containous/whoami:v1.3.0 + hostname: whoami3 +whoamitcp: + image: containous/whoamitcp + hostname: whoamitcp diff --git a/pkg/config/static/static_config.go b/pkg/config/static/static_config.go index 6921425c7..93f970ff3 100644 --- a/pkg/config/static/static_config.go +++ b/pkg/config/static/static_config.go @@ -9,6 +9,7 @@ import ( "github.com/containous/traefik/v2/pkg/log" "github.com/containous/traefik/v2/pkg/ping" acmeprovider "github.com/containous/traefik/v2/pkg/provider/acme" + "github.com/containous/traefik/v2/pkg/provider/consulcatalog" "github.com/containous/traefik/v2/pkg/provider/docker" "github.com/containous/traefik/v2/pkg/provider/file" "github.com/containous/traefik/v2/pkg/provider/kubernetes/crd" @@ -153,14 +154,15 @@ func (t *Tracing) SetDefaults() { // Providers contains providers configuration type Providers struct { - ProvidersThrottleDuration types.Duration `description:"Backends throttle duration: minimum duration between 2 events from providers before applying a new configuration. It avoids unnecessary reloads if multiples events are sent in a short amount of time." json:"providersThrottleDuration,omitempty" toml:"providersThrottleDuration,omitempty" yaml:"providersThrottleDuration,omitempty" export:"true"` - Docker *docker.Provider `description:"Enable Docker backend with default settings." json:"docker,omitempty" toml:"docker,omitempty" yaml:"docker,omitempty" export:"true" label:"allowEmpty"` - File *file.Provider `description:"Enable File backend with default settings." json:"file,omitempty" toml:"file,omitempty" yaml:"file,omitempty" export:"true"` - Marathon *marathon.Provider `description:"Enable Marathon backend with default settings." json:"marathon,omitempty" toml:"marathon,omitempty" yaml:"marathon,omitempty" export:"true" label:"allowEmpty"` - KubernetesIngress *ingress.Provider `description:"Enable Kubernetes backend with default settings." json:"kubernetesIngress,omitempty" toml:"kubernetesIngress,omitempty" yaml:"kubernetesIngress,omitempty" export:"true" label:"allowEmpty"` - KubernetesCRD *crd.Provider `description:"Enable Kubernetes backend with default settings." json:"kubernetesCRD,omitempty" toml:"kubernetesCRD,omitempty" yaml:"kubernetesCRD,omitempty" export:"true" label:"allowEmpty"` - Rest *rest.Provider `description:"Enable Rest backend with default settings." json:"rest,omitempty" toml:"rest,omitempty" yaml:"rest,omitempty" export:"true" label:"allowEmpty"` - Rancher *rancher.Provider `description:"Enable Rancher backend with default settings." json:"rancher,omitempty" toml:"rancher,omitempty" yaml:"rancher,omitempty" export:"true" label:"allowEmpty"` + ProvidersThrottleDuration types.Duration `description:"Backends throttle duration: minimum duration between 2 events from providers before applying a new configuration. It avoids unnecessary reloads if multiples events are sent in a short amount of time." json:"providersThrottleDuration,omitempty" toml:"providersThrottleDuration,omitempty" yaml:"providersThrottleDuration,omitempty" export:"true"` + Docker *docker.Provider `description:"Enable Docker backend with default settings." json:"docker,omitempty" toml:"docker,omitempty" yaml:"docker,omitempty" export:"true" label:"allowEmpty"` + File *file.Provider `description:"Enable File backend with default settings." json:"file,omitempty" toml:"file,omitempty" yaml:"file,omitempty" export:"true"` + Marathon *marathon.Provider `description:"Enable Marathon backend with default settings." json:"marathon,omitempty" toml:"marathon,omitempty" yaml:"marathon,omitempty" export:"true" label:"allowEmpty"` + KubernetesIngress *ingress.Provider `description:"Enable Kubernetes backend with default settings." json:"kubernetesIngress,omitempty" toml:"kubernetesIngress,omitempty" yaml:"kubernetesIngress,omitempty" export:"true" label:"allowEmpty"` + KubernetesCRD *crd.Provider `description:"Enable Kubernetes backend with default settings." json:"kubernetesCRD,omitempty" toml:"kubernetesCRD,omitempty" yaml:"kubernetesCRD,omitempty" export:"true" label:"allowEmpty"` + Rest *rest.Provider `description:"Enable Rest backend with default settings." json:"rest,omitempty" toml:"rest,omitempty" yaml:"rest,omitempty" export:"true" label:"allowEmpty"` + Rancher *rancher.Provider `description:"Enable Rancher backend with default settings." json:"rancher,omitempty" toml:"rancher,omitempty" yaml:"rancher,omitempty" export:"true" label:"allowEmpty"` + ConsulCatalog *consulcatalog.Provider `description:"Enable ConsulCatalog backend with default settings." json:"consulcatalog,omitempty" toml:"consulcatalog,omitempty" yaml:"consulcatalog,omitempty"` } // SetEffectiveConfiguration adds missing configuration parameters derived from existing ones. diff --git a/pkg/provider/aggregator/aggregator.go b/pkg/provider/aggregator/aggregator.go index 5cc7ede82..7bcdee70c 100644 --- a/pkg/provider/aggregator/aggregator.go +++ b/pkg/provider/aggregator/aggregator.go @@ -49,6 +49,10 @@ func NewProviderAggregator(conf static.Providers) ProviderAggregator { p.quietAddProvider(conf.Rancher) } + if conf.ConsulCatalog != nil { + p.quietAddProvider(conf.ConsulCatalog) + } + return p } diff --git a/pkg/provider/consulcatalog/config.go b/pkg/provider/consulcatalog/config.go new file mode 100644 index 000000000..31933bb4f --- /dev/null +++ b/pkg/provider/consulcatalog/config.go @@ -0,0 +1,200 @@ +package consulcatalog + +import ( + "context" + "errors" + "fmt" + "net" + + "github.com/containous/traefik/v2/pkg/config/dynamic" + "github.com/containous/traefik/v2/pkg/config/label" + "github.com/containous/traefik/v2/pkg/log" + "github.com/containous/traefik/v2/pkg/provider" + "github.com/containous/traefik/v2/pkg/provider/constraints" + "github.com/hashicorp/consul/api" +) + +func (p *Provider) buildConfiguration(ctx context.Context, items []itemData) *dynamic.Configuration { + configurations := make(map[string]*dynamic.Configuration) + + for _, item := range items { + svcName := item.Name + "-" + item.ID + ctxSvc := log.With(ctx, log.Str("serviceName", svcName)) + + if !p.keepContainer(ctxSvc, item) { + continue + } + + logger := log.FromContext(ctxSvc) + + confFromLabel, err := label.DecodeConfiguration(item.Labels) + if err != nil { + logger.Error(err) + continue + } + + if len(confFromLabel.TCP.Routers) > 0 || len(confFromLabel.TCP.Services) > 0 { + err := p.buildTCPServiceConfiguration(ctxSvc, item, confFromLabel.TCP) + if err != nil { + logger.Error(err) + continue + } + + provider.BuildTCPRouterConfiguration(ctxSvc, confFromLabel.TCP) + + if len(confFromLabel.HTTP.Routers) == 0 && + len(confFromLabel.HTTP.Middlewares) == 0 && + len(confFromLabel.HTTP.Services) == 0 { + configurations[svcName] = confFromLabel + continue + } + } + + err = p.buildServiceConfiguration(ctxSvc, item, confFromLabel.HTTP) + if err != nil { + logger.Error(err) + continue + } + + model := struct { + Name string + Labels map[string]string + }{ + Name: item.Name, + Labels: item.Labels, + } + + provider.BuildRouterConfiguration(ctx, confFromLabel.HTTP, item.Name, p.defaultRuleTpl, model) + + configurations[svcName] = confFromLabel + } + + return provider.Merge(ctx, configurations) +} + +func (p *Provider) keepContainer(ctx context.Context, item itemData) bool { + logger := log.FromContext(ctx) + + if !item.ExtraConf.Enable { + logger.Debug("Filtering disabled item") + return false + } + + matches, err := constraints.Match(item.Labels, p.Constraints) + if err != nil { + logger.Errorf("Error matching constraints expression: %v", err) + return false + } + if !matches { + logger.Debugf("Container pruned by constraint expression: %q", p.Constraints) + return false + } + + if item.Status != api.HealthPassing && item.Status != api.HealthWarning { + logger.Debug("Filtering unhealthy or starting item") + return false + } + + return true +} + +func (p *Provider) buildTCPServiceConfiguration(ctx context.Context, item itemData, configuration *dynamic.TCPConfiguration) error { + if len(configuration.Services) == 0 { + configuration.Services = make(map[string]*dynamic.TCPService) + + lb := &dynamic.TCPServersLoadBalancer{} + lb.SetDefaults() + + configuration.Services[item.Name] = &dynamic.TCPService{ + LoadBalancer: lb, + } + } + + for name, service := range configuration.Services { + ctxSvc := log.With(ctx, log.Str(log.ServiceName, name)) + err := p.addServerTCP(ctxSvc, item, service.LoadBalancer) + if err != nil { + return err + } + } + + return nil +} + +func (p *Provider) buildServiceConfiguration(ctx context.Context, item itemData, configuration *dynamic.HTTPConfiguration) error { + if len(configuration.Services) == 0 { + configuration.Services = make(map[string]*dynamic.Service) + + lb := &dynamic.ServersLoadBalancer{} + lb.SetDefaults() + + configuration.Services[item.Name] = &dynamic.Service{ + LoadBalancer: lb, + } + } + + for name, service := range configuration.Services { + ctxSvc := log.With(ctx, log.Str(log.ServiceName, name)) + err := p.addServer(ctxSvc, item, service.LoadBalancer) + if err != nil { + return err + } + } + + return nil +} + +func (p *Provider) addServerTCP(ctx context.Context, item itemData, loadBalancer *dynamic.TCPServersLoadBalancer) error { + if loadBalancer == nil { + return errors.New("load-balancer is not defined") + } + + if len(loadBalancer.Servers) == 0 { + loadBalancer.Servers = []dynamic.TCPServer{{}} + } + + var port string + if item.Port != "" { + port = item.Port + loadBalancer.Servers[0].Port = "" + } + + if port == "" { + return errors.New("port is missing") + } + + loadBalancer.Servers[0].Address = net.JoinHostPort(item.Address, port) + return nil +} + +func (p *Provider) addServer(ctx context.Context, item itemData, loadBalancer *dynamic.ServersLoadBalancer) error { + if loadBalancer == nil { + return errors.New("load-balancer is not defined") + } + + var port string + if len(loadBalancer.Servers) > 0 { + port = loadBalancer.Servers[0].Port + } + + if len(loadBalancer.Servers) == 0 { + server := dynamic.Server{} + server.SetDefaults() + + loadBalancer.Servers = []dynamic.Server{server} + } + + if item.Port != "" { + port = item.Port + loadBalancer.Servers[0].Port = "" + } + + if port == "" { + return errors.New("port is missing") + } + + loadBalancer.Servers[0].URL = fmt.Sprintf("%s://%s", loadBalancer.Servers[0].Scheme, net.JoinHostPort(item.Address, port)) + loadBalancer.Servers[0].Scheme = "" + + return nil +} diff --git a/pkg/provider/consulcatalog/config_test.go b/pkg/provider/consulcatalog/config_test.go new file mode 100644 index 000000000..a794bd90e --- /dev/null +++ b/pkg/provider/consulcatalog/config_test.go @@ -0,0 +1,1850 @@ +package consulcatalog + +import ( + "context" + "testing" + + "github.com/containous/traefik/v2/pkg/config/dynamic" + "github.com/hashicorp/consul/api" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Int(v int) *int { return &v } +func Bool(v bool) *bool { return &v } + +func TestDefaultRule(t *testing.T) { + testCases := []struct { + desc string + items []itemData + defaultRule string + expected *dynamic.Configuration + }{ + { + desc: "default rule with no variable", + items: []itemData{ + { + ID: "id", + Name: "Test", + Address: "127.0.0.1", + Port: "80", + Labels: nil, + Status: api.HealthPassing, + }, + }, + defaultRule: "Host(`foo.bar`)", + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Test", + Rule: "Host(`foo.bar`)", + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "default rule with label", + items: []itemData{ + { + ID: "id", + Name: "Test", + Address: "127.0.0.1", + Port: "80", + Labels: map[string]string{ + "traefik.domain": "foo.bar", + }, + Status: api.HealthPassing, + }, + }, + defaultRule: `Host("{{ .Name }}.{{ index .Labels "traefik.domain" }}")`, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Test", + Rule: `Host("Test.foo.bar")`, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "invalid rule", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{}, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + }, + defaultRule: `Host("{{ .Toto }}")`, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "undefined rule", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{}, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + }, + defaultRule: ``, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "default template rule", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{}, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + }, + defaultRule: DefaultTemplateRule, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Test", + Rule: "Host(`Test`)", + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + p := Provider{ + ExposedByDefault: true, + DefaultRule: test.defaultRule, + } + + err := p.Init() + require.NoError(t, err) + + for i := 0; i < len(test.items); i++ { + var err error + test.items[i].ExtraConf, err = p.getConfiguration(test.items[i]) + require.NoError(t, err) + } + + configuration := p.buildConfiguration(context.Background(), test.items) + + assert.Equal(t, test.expected, configuration) + }) + } +} + +func Test_buildConfiguration(t *testing.T) { + testCases := []struct { + desc string + items []itemData + constraints string + expected *dynamic.Configuration + }{ + { + desc: "one container no label", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{}, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Test", + Rule: "Host(`Test.traefik.wtf`)", + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "two containers no label", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{}, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + { + ID: "Test2", + Name: "Test2", + Labels: map[string]string{}, + Address: "127.0.0.2", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Test", + Rule: "Host(`Test.traefik.wtf`)", + }, + "Test2": { + Service: "Test2", + Rule: "Host(`Test2.traefik.wtf`)", + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + "Test2": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.2:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "two containers with same service name no label", + items: []itemData{ + { + ID: "1", + Name: "Test", + Labels: map[string]string{}, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + { + ID: "2", + Name: "Test", + Labels: map[string]string{}, + Address: "127.0.0.2", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Test", + Rule: "Host(`Test.traefik.wtf`)", + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + { + URL: "http://127.0.0.2:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "one container with label (not on server)", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{ + "traefik.http.services.Service1.loadbalancer.passhostheader": "true", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Service1", + Rule: "Host(`Test.traefik.wtf`)", + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Service1": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "one container with labels", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{ + "traefik.http.services.Service1.loadbalancer.passhostheader": "true", + "traefik.http.routers.Router1.rule": "Host(`foo.com`)", + "traefik.http.routers.Router1.service": "Service1", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Router1": { + Service: "Service1", + Rule: "Host(`foo.com`)", + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Service1": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "one container with rule label", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{ + "traefik.http.routers.Router1.rule": "Host(`foo.com`)", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + Routers: map[string]*dynamic.Router{ + "Router1": { + Service: "Test", + Rule: "Host(`foo.com`)", + }, + }, + }, + }, + }, + { + desc: "one container with rule label and one service", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{ + "traefik.http.routers.Router1.rule": "Host(`foo.com`)", + "traefik.http.services.Service1.loadbalancer.passhostheader": "true", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Router1": { + Service: "Service1", + Rule: "Host(`foo.com`)", + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Service1": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "one container with rule label and two services", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{ + "traefik.http.routers.Router1.rule": "Host(`foo.com`)", + "traefik.http.services.Service1.loadbalancer.passhostheader": "true", + "traefik.http.services.Service2.loadbalancer.passhostheader": "true", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Service1": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + "Service2": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "two containers with same service name and different passhostheader", + items: []itemData{ + { + ID: "1", + Name: "Test", + Labels: map[string]string{ + "traefik.http.services.Service1.loadbalancer.passhostheader": "true", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + { + ID: "2", + Name: "Test", + Labels: map[string]string{ + "traefik.http.services.Service1.loadbalancer.passhostheader": "false", + }, Address: "127.0.0.2", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Service1", + Rule: "Host(`Test.traefik.wtf`)", + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + }, + }, + { + desc: "three containers with same service name and different passhostheader", + items: []itemData{ + { + ID: "1", + Name: "Test", + Labels: map[string]string{ + "traefik.http.services.Service1.loadbalancer.passhostheader": "false", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + { + ID: "2", + Name: "Test", + Labels: map[string]string{ + "traefik.http.services.Service1.loadbalancer.passhostheader": "true", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + { + ID: "3", + Name: "Test", + Labels: map[string]string{ + "traefik.http.services.Service1.loadbalancer.passhostheader": "true", + }, + Address: "127.0.0.2", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Service1", + Rule: "Host(`Test.traefik.wtf`)", + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + }, + }, + { + desc: "two containers with same service name and same LB methods", + items: []itemData{ + { + ID: "1", + Name: "Test", + Labels: map[string]string{ + "traefik.http.services.Service1.loadbalancer.passhostheader": "true", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + { + ID: "2", + Name: "Test", + Labels: map[string]string{ + "traefik.http.services.Service1.loadbalancer.passhostheader": "true", + }, + Address: "127.0.0.2", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Service1", + Rule: "Host(`Test.traefik.wtf`)", + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Service1": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + { + URL: "http://127.0.0.2:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "one container with InFlightReq in label (default value)", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{ + "traefik.http.middlewares.Middleware1.inflightreq.amount": "42", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Test", + Rule: "Host(`Test.traefik.wtf`)", + }, + }, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + Middlewares: map[string]*dynamic.Middleware{ + "Middleware1": { + InFlightReq: &dynamic.InFlightReq{ + Amount: 42, + SourceCriterion: &dynamic.SourceCriterion{ + RequestHost: true, + }, + }, + }, + }, + }, + }, + }, + { + desc: "two services with two identical middlewares", + items: []itemData{ + { + ID: "1", + Name: "Test", + Labels: map[string]string{ + "traefik.http.middlewares.Middleware1.inflightreq.amount": "42", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + { + ID: "2", + Name: "Test", + Labels: map[string]string{ + "traefik.http.middlewares.Middleware1.inflightreq.amount": "42", + }, Address: "127.0.0.2", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Test", + Rule: "Host(`Test.traefik.wtf`)", + }, + }, + Middlewares: map[string]*dynamic.Middleware{ + "Middleware1": { + InFlightReq: &dynamic.InFlightReq{ + Amount: 42, + SourceCriterion: &dynamic.SourceCriterion{ + RequestHost: true, + }, + }, + }, + }, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + { + URL: "http://127.0.0.2:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "two containers with two different middlewares with same name", + items: []itemData{ + { + ID: "1", + Name: "Test", + Labels: map[string]string{ + "traefik.http.middlewares.Middleware1.inflightreq.amount": "42", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + { + ID: "2", + Name: "Test", + Labels: map[string]string{ + "traefik.http.middlewares.Middleware1.inflightreq.amount": "41", + }, Address: "127.0.0.2", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Test", + Rule: "Host(`Test.traefik.wtf`)", + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + { + URL: "http://127.0.0.2:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "three containers with different middlewares with same name", + items: []itemData{ + { + ID: "1", + Name: "Test", + Labels: map[string]string{ + "traefik.http.middlewares.Middleware1.inflightreq.amount": "42", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + { + ID: "2", + + Name: "Test", + Labels: map[string]string{ + "traefik.http.middlewares.Middleware1.inflightreq.amount": "41", + }, + Address: "127.0.0.2", + Port: "80", + Status: api.HealthPassing, + }, + { + ID: "3", + + Name: "Test", + Labels: map[string]string{ + "traefik.http.middlewares.Middleware1.inflightreq.amount": "40", + }, + Address: "127.0.0.3", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Test", + Rule: "Host(`Test.traefik.wtf`)", + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + { + URL: "http://127.0.0.2:80", + }, + { + URL: "http://127.0.0.3:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "two containers with two different routers with same name", + items: []itemData{ + { + ID: "1", + + Name: "Test", + Labels: map[string]string{ + "traefik.http.routers.Router1.rule": "Host(`foo.com`)", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + { + ID: "2", + + Name: "Test", + Labels: map[string]string{ + "traefik.http.routers.Router1.rule": "Host(`bar.com`)", + }, Address: "127.0.0.2", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + { + URL: "http://127.0.0.2:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "three containers with different routers with same name", + items: []itemData{ + { + ID: "1", + + Name: "Test", + Labels: map[string]string{ + "traefik.http.routers.Router1.rule": "Host(`foo.com`)", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + { + ID: "2", + + Name: "Test", + Labels: map[string]string{ + "traefik.http.routers.Router1.rule": "Host(`bar.com`)", + }, Address: "127.0.0.2", + Port: "80", + Status: api.HealthPassing, + }, + { + ID: "3", + + Name: "Test", + Labels: map[string]string{ + "traefik.http.routers.Router1.rule": "Host(`foobar.com`)", + }, + Address: "127.0.0.3", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + { + URL: "http://127.0.0.2:80", + }, + { + URL: "http://127.0.0.3:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "two containers with two identical routers", + items: []itemData{ + { + ID: "1", + + Name: "Test", + Labels: map[string]string{ + "traefik.http.routers.Router1.rule": "Host(`foo.com`)", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + { + ID: "2", + + Name: "Test", + Labels: map[string]string{ + "traefik.http.routers.Router1.rule": "Host(`foo.com`)", + }, + Address: "127.0.0.2", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Router1": { + Service: "Test", + Rule: "Host(`foo.com`)", + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + { + URL: "http://127.0.0.2:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "one container with bad label", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{ + "traefik.wrong.label": "42", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Test", + Rule: "Host(`Test.traefik.wtf`)", + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "one container with label port", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{ + "traefik.http.services.Service1.LoadBalancer.server.scheme": "h2c", + "traefik.http.services.Service1.LoadBalancer.server.port": "8080", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Service1", + Rule: "Host(`Test.traefik.wtf`)", + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Service1": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "h2c://127.0.0.1:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "one container with label port on two services", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{ + "traefik.http.services.Service1.LoadBalancer.server.port": "", + "traefik.http.services.Service2.LoadBalancer.server.port": "8080", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Service1": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + "Service2": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "one container without port", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{}, + Address: "127.0.0.2", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + }, + }, + { + desc: "one container without port with middleware", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{ + "traefik.http.middlewares.Middleware1.inflightreq.amount": "42", + }, + Address: "127.0.0.2", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + }, + }, + { + desc: "one container with traefik.enable false", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{ + "traefik.enable": "false", + }, + Address: "127.0.0.1", + Port: "80", + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + }, + }, + { + desc: "one container not healthy", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{}, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthCritical, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + }, + }, + { + desc: "one container with non matching constraints", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{ + "traefik.tags": "foo", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + }, + constraints: `Label("traefik.tags", "bar")`, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + }, + }, + { + desc: "one container with matching constraints", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{ + "traefik.tags": "foo", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + }, + constraints: `Label("traefik.tags", "foo")`, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Test", + Rule: "Host(`Test.traefik.wtf`)", + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "Middlewares used in router", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{ + "traefik.http.middlewares.Middleware1.basicauth.users": "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", + "traefik.http.routers.Test.middlewares": "Middleware1", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Test", + Rule: "Host(`Test.traefik.wtf`)", + Middlewares: []string{"Middleware1"}, + }, + }, + Middlewares: map[string]*dynamic.Middleware{ + "Middleware1": { + BasicAuth: &dynamic.BasicAuth{ + Users: []string{ + "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", + "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", + }, + }, + }, + }, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "tcp with label", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{ + "traefik.tcp.routers.foo.rule": "HostSNI(`foo.bar`)", + "traefik.tcp.routers.foo.tls": "true", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{ + "foo": { + Service: "Test", + Rule: "HostSNI(`foo.bar`)", + TLS: &dynamic.RouterTCPTLSConfig{}, + }, + }, + Services: map[string]*dynamic.TCPService{ + "Test": { + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.1:80", + }, + }, + TerminationDelay: Int(100), + }, + }, + }, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + }, + }, + { + desc: "tcp with label without rule", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{ + "traefik.tcp.routers.foo.tls": "true", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{ + "Test": { + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.1:80", + }, + }, + TerminationDelay: Int(100), + }, + }, + }, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + }, + }, + { + desc: "tcp with label and port", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{ + "traefik.tcp.routers.foo.rule": "HostSNI(`foo.bar`)", + "traefik.tcp.routers.foo.tls.options": "foo", + "traefik.tcp.services.foo.loadbalancer.server.port": "80", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{ + "foo": { + Service: "foo", + Rule: "HostSNI(`foo.bar`)", + TLS: &dynamic.RouterTCPTLSConfig{ + Options: "foo", + }, + }, + }, + Services: map[string]*dynamic.TCPService{ + "foo": { + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.1:80", + }, + }, + TerminationDelay: Int(100), + }, + }, + }, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + }, + }, + { + desc: "tcp with label and port and http service", + items: []itemData{ + { + ID: "1", + Name: "Test", + Labels: map[string]string{ + "traefik.tcp.routers.foo.rule": "HostSNI(`foo.bar`)", + "traefik.tcp.routers.foo.tls": "true", + "traefik.tcp.services.foo.loadbalancer.server.port": "80", + "traefik.http.services.Service1.loadbalancer.passhostheader": "true", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + { + ID: "2", + Name: "Test", + Labels: map[string]string{ + "traefik.tcp.routers.foo.rule": "HostSNI(`foo.bar`)", + "traefik.tcp.routers.foo.tls": "true", + "traefik.tcp.services.foo.loadbalancer.server.port": "80", + "traefik.http.services.Service1.loadbalancer.passhostheader": "true", + }, + Address: "127.0.0.2", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{ + "foo": { + Service: "foo", + Rule: "HostSNI(`foo.bar`)", + TLS: &dynamic.RouterTCPTLSConfig{}, + }, + }, + Services: map[string]*dynamic.TCPService{ + "foo": { + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.1:80", + }, + { + Address: "127.0.0.2:80", + }, + }, + TerminationDelay: Int(100), + }, + }, + }, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Service1", + Rule: "Host(`Test.traefik.wtf`)", + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Service1": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + { + URL: "http://127.0.0.2:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "tcp with label for tcp service", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{ + "traefik.tcp.services.foo.loadbalancer.server.port": "80", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{ + "foo": { + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.1:80", + }, + }, + TerminationDelay: Int(100), + }, + }, + }, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + }, + }, + { + desc: "tcp with label for tcp service, with termination delay", + items: []itemData{ + { + ID: "Test", + Name: "Test", + Labels: map[string]string{ + "traefik.tcp.services.foo.loadbalancer.server.port": "80", + "traefik.tcp.services.foo.loadbalancer.terminationdelay": "200", + }, + Address: "127.0.0.1", + Port: "80", + Status: api.HealthPassing, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{ + "foo": { + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.1:80", + }, + }, + TerminationDelay: Int(200), + }, + }, + }, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + }, + }, + } + + for _, test := range testCases { + test := test + + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + p := Provider{ + ExposedByDefault: true, + DefaultRule: "Host(`{{ normalize .Name }}.traefik.wtf`)", + } + p.Constraints = test.constraints + + err := p.Init() + require.NoError(t, err) + + for i := 0; i < len(test.items); i++ { + var err error + test.items[i].ExtraConf, err = p.getConfiguration(test.items[i]) + require.NoError(t, err) + } + + configuration := p.buildConfiguration(context.Background(), test.items) + + assert.Equal(t, test.expected, configuration) + }) + } +} diff --git a/pkg/provider/consulcatalog/consul_catalog.go b/pkg/provider/consulcatalog/consul_catalog.go new file mode 100644 index 000000000..255d4116f --- /dev/null +++ b/pkg/provider/consulcatalog/consul_catalog.go @@ -0,0 +1,221 @@ +package consulcatalog + +import ( + "context" + "fmt" + "strconv" + "text/template" + "time" + + "github.com/cenkalti/backoff/v3" + "github.com/containous/traefik/v2/pkg/config/dynamic" + "github.com/containous/traefik/v2/pkg/job" + "github.com/containous/traefik/v2/pkg/log" + "github.com/containous/traefik/v2/pkg/provider" + "github.com/containous/traefik/v2/pkg/safe" + "github.com/containous/traefik/v2/pkg/types" + "github.com/hashicorp/consul/api" +) + +// DefaultTemplateRule The default template for the default rule. +const DefaultTemplateRule = "Host(`{{ normalize .Name }}`)" + +var _ provider.Provider = (*Provider)(nil) + +type itemData struct { + ID string + Name string + Address string + Port string + Status string + Labels map[string]string + ExtraConf configuration +} + +// Provider holds configurations of the provider. +type Provider struct { + Constraints string `description:"Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container." json:"constraints,omitempty" toml:"constraints,omitempty" yaml:"constraints,omitempty" export:"true"` + Endpoint *EndpointConfig `description:"Consul endpoint settings" json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty" export:"true"` + Prefix string `description:"Prefix for consul service tags. Default 'traefik'" json:"prefix,omitempty" toml:"prefix,omitempty" yaml:"prefix,omitempty" export:"true"` + RefreshInterval types.Duration `description:"Interval for check Consul API. Default 100ms" json:"refreshInterval,omitempty" toml:"refreshInterval,omitempty" yaml:"refreshInterval,omitempty" export:"true"` + ExposedByDefault bool `description:"Expose containers by default." json:"exposedByDefault,omitempty" toml:"exposedByDefault,omitempty" yaml:"exposedByDefault,omitempty" export:"true"` + DefaultRule string `description:"Default rule." json:"defaultRule,omitempty" toml:"defaultRule,omitempty" yaml:"defaultRule,omitempty"` + + client *api.Client + defaultRuleTpl *template.Template +} + +// EndpointConfig holds configurations of the endpoint. +type EndpointConfig struct { + Address string `description:"The address of the Consul server" json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty" export:"true"` + Scheme string `description:"The URI scheme for the Consul server" json:"scheme,omitempty" toml:"scheme,omitempty" yaml:"scheme,omitempty" export:"true"` + DataCenter string `description:"Data center to use. If not provided, the default agent data center is used" json:"data center,omitempty" toml:"data center,omitempty" yaml:"datacenter,omitempty" export:"true"` + Token string `description:"Token is used to provide a per-request ACL token which overrides the agent's default token" json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" export:"true"` + TLS *types.ClientTLS `description:"Enable TLS support." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"` + HTTPAuth *EndpointHTTPAuthConfig `description:"Auth info to use for http access" json:"httpAuth,omitempty" toml:"httpAuth,omitempty" yaml:"httpAuth,omitempty" export:"true"` + EndpointWaitTime types.Duration `description:"WaitTime limits how long a Watch will block. If not provided, the agent default values will be used" json:"endpointWaitTime,omitempty" toml:"endpointWaitTime,omitempty" yaml:"endpointWaitTime,omitempty" export:"true"` +} + +// SetDefaults sets the default values. +func (c *EndpointConfig) SetDefaults() { + c.Address = "http://127.0.0.1:8500" +} + +// EndpointHTTPAuthConfig holds configurations of the authentication. +type EndpointHTTPAuthConfig struct { + Username string `description:"Basic Auth username" json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty" export:"true"` + Password string `description:"Basic Auth password" json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty" export:"true"` +} + +// SetDefaults sets the default values. +func (p *Provider) SetDefaults() { + endpoint := &EndpointConfig{} + endpoint.SetDefaults() + p.Endpoint = endpoint + p.RefreshInterval = types.Duration(15 * time.Second) + p.Prefix = "traefik" + p.ExposedByDefault = true + p.DefaultRule = DefaultTemplateRule +} + +// Init the provider. +func (p *Provider) Init() error { + defaultRuleTpl, err := provider.MakeDefaultRuleTemplate(p.DefaultRule, nil) + if err != nil { + return fmt.Errorf("error while parsing default rule: %v", err) + } + + p.defaultRuleTpl = defaultRuleTpl + return nil +} + +// Provide allows the consul catalog provider to provide configurations to traefik using the given configuration channel. +func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error { + pool.GoCtx(func(routineCtx context.Context) { + ctxLog := log.With(routineCtx, log.Str(log.ProviderName, "consulcatalog")) + logger := log.FromContext(ctxLog) + + operation := func() error { + var err error + + p.client, err = createClient(p.Endpoint) + if err != nil { + return fmt.Errorf("error create consul client, %v", err) + } + + ticker := time.NewTicker(time.Duration(p.RefreshInterval)) + + for { + select { + case <-ticker.C: + data, err := p.getConsulServicesData(routineCtx) + if err != nil { + logger.Errorf("error get consulCatalog data, %v", err) + return err + } + + configuration := p.buildConfiguration(routineCtx, data) + configurationChan <- dynamic.Message{ + ProviderName: "consulcatalog", + Configuration: configuration, + } + case <-routineCtx.Done(): + ticker.Stop() + return nil + } + } + } + + notify := func(err error, time time.Duration) { + logger.Errorf("Provider connection error %+v, retrying in %s", err, time) + } + + err := backoff.RetryNotify(safe.OperationWithRecover(operation), backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), ctxLog), notify) + if err != nil { + logger.Errorf("Cannot connect to consulcatalog server %+v", err) + } + }) + + return nil +} + +func (p *Provider) getConsulServicesData(ctx context.Context) ([]itemData, error) { + consulServiceNames, err := p.fetchServices(ctx) + if err != nil { + return nil, err + } + + var data []itemData + for name := range consulServiceNames { + consulServices, err := p.fetchService(ctx, name) + if err != nil { + return nil, err + } + + for _, consulService := range consulServices { + labels := tagsToNeutralLabels(consulService.ServiceTags, p.Prefix) + item := itemData{ + ID: consulService.ServiceID, + Name: consulService.ServiceName, + Address: consulService.ServiceAddress, + Port: strconv.Itoa(consulService.ServicePort), + Labels: labels, + Status: consulService.Checks.AggregatedStatus(), + } + + extraConf, err := p.getConfiguration(item) + if err != nil { + log.FromContext(ctx).Errorf("Skip item %s: %v", item.Name, err) + continue + } + item.ExtraConf = extraConf + + data = append(data, item) + } + } + return data, nil +} + +func (p *Provider) fetchService(ctx context.Context, name string) ([]*api.CatalogService, error) { + var tagFilter string + if !p.ExposedByDefault { + tagFilter = p.Prefix + ".enable=true" + } + + consulServices, _, err := p.client.Catalog().Service(name, tagFilter, &api.QueryOptions{}) + return consulServices, err +} + +func (p *Provider) fetchServices(ctx context.Context) (map[string][]string, error) { + serviceNames, _, err := p.client.Catalog().Services(&api.QueryOptions{}) + return serviceNames, err +} + +func createClient(cfg *EndpointConfig) (*api.Client, error) { + config := api.Config{ + Address: cfg.Address, + Scheme: cfg.Scheme, + Datacenter: cfg.DataCenter, + WaitTime: time.Duration(cfg.EndpointWaitTime), + Token: cfg.Token, + } + + if cfg.HTTPAuth != nil { + config.HttpAuth = &api.HttpBasicAuth{ + Username: cfg.HTTPAuth.Username, + Password: cfg.HTTPAuth.Password, + } + } + + if cfg.TLS != nil { + config.TLSConfig = api.TLSConfig{ + Address: cfg.Address, + CAFile: cfg.TLS.CA, + CertFile: cfg.TLS.Cert, + KeyFile: cfg.TLS.Key, + InsecureSkipVerify: cfg.TLS.InsecureSkipVerify, + } + } + + return api.NewClient(&config) +} diff --git a/pkg/provider/consulcatalog/convert_types.go b/pkg/provider/consulcatalog/convert_types.go new file mode 100644 index 000000000..820462d62 --- /dev/null +++ b/pkg/provider/consulcatalog/convert_types.go @@ -0,0 +1,26 @@ +package consulcatalog + +import ( + "strings" +) + +func tagsToNeutralLabels(tags []string, prefix string) map[string]string { + var labels map[string]string + + for _, tag := range tags { + if strings.HasPrefix(tag, prefix) { + parts := strings.SplitN(tag, "=", 2) + if len(parts) == 2 { + if labels == nil { + labels = make(map[string]string) + } + + // replace custom prefix by the generic prefix + key := "traefik." + strings.TrimPrefix(parts[0], prefix+".") + labels[key] = parts[1] + } + } + } + + return labels +} diff --git a/pkg/provider/consulcatalog/convert_types_test.go b/pkg/provider/consulcatalog/convert_types_test.go new file mode 100644 index 000000000..b0c24b02d --- /dev/null +++ b/pkg/provider/consulcatalog/convert_types_test.go @@ -0,0 +1,64 @@ +package consulcatalog + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestTagsToNeutralLabels(t *testing.T) { + testCases := []struct { + desc string + tags []string + prefix string + expected map[string]string + }{ + { + desc: "without tags", + expected: nil, + }, + { + desc: "with a prefix", + prefix: "test", + tags: []string{ + "test.aaa=01", + "test.bbb=02", + "ccc=03", + "test.ddd=04=to", + }, + expected: map[string]string{ + "traefik.aaa": "01", + "traefik.bbb": "02", + "traefik.ddd": "04=to", + }, + }, + + { + desc: "with an empty prefix", + prefix: "", + tags: []string{ + "test.aaa=01", + "test.bbb=02", + "ccc=03", + "test.ddd=04=to", + }, + expected: map[string]string{ + "traefik.test.aaa": "01", + "traefik.test.bbb": "02", + "traefik.ccc": "03", + "traefik.test.ddd": "04=to", + }, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + labels := tagsToNeutralLabels(test.tags, test.prefix) + + assert.Equal(t, test.expected, labels) + }) + } +} diff --git a/pkg/provider/consulcatalog/label.go b/pkg/provider/consulcatalog/label.go new file mode 100644 index 000000000..a60ae96c0 --- /dev/null +++ b/pkg/provider/consulcatalog/label.go @@ -0,0 +1,23 @@ +package consulcatalog + +import ( + "github.com/containous/traefik/v2/pkg/config/label" +) + +// configuration Contains information from the labels that are globals (not related to the dynamic configuration) or specific to the provider. +type configuration struct { + Enable bool +} + +func (p *Provider) getConfiguration(item itemData) (configuration, error) { + conf := configuration{ + Enable: p.ExposedByDefault, + } + + err := label.Decode(item.Labels, &conf, "traefik.consulcatalog.", "traefik.enable") + if err != nil { + return configuration{}, err + } + + return conf, nil +} diff --git a/webui/src/statics/providers/consulcatalog.svg b/webui/src/statics/providers/consulcatalog.svg new file mode 100644 index 000000000..28bbadd24 --- /dev/null +++ b/webui/src/statics/providers/consulcatalog.svg @@ -0,0 +1 @@ +Asset 1 \ No newline at end of file