Implement Traefik provider for Nomad orchestrator
This commit is contained in:
parent
becee5e393
commit
aa0b5466a9
23 changed files with 4328 additions and 7 deletions
BIN
docs/content/assets/img/providers/nomad.png
Normal file
BIN
docs/content/assets/img/providers/nomad.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
544
docs/content/providers/nomad.md
Normal file
544
docs/content/providers/nomad.md
Normal file
|
@ -0,0 +1,544 @@
|
|||
---
|
||||
title: "Nomad Service Discovery"
|
||||
description: "Learn how to use Nomad as a provider for configuration discovery in Traefik Proxy. Read the technical documentation."
|
||||
---
|
||||
|
||||
# Traefik & Nomad Service Discovery
|
||||
|
||||
A Story of Tags, Services & Nomads
|
||||
{: .subtitle }
|
||||
|
||||
![Nomad Service Discovery](../assets/img/providers/nomad.png)
|
||||
|
||||
Attach tags to your Nomad services and let Traefik do the rest!
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
??? example "Configuring Nomad & Deploying Services"
|
||||
|
||||
Enabling the nomad provider
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
nomad: {}
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.nomad]
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.nomad=true
|
||||
```
|
||||
|
||||
Attaching tags to services:
|
||||
|
||||
```
|
||||
...
|
||||
service {
|
||||
name = "myService"
|
||||
tags = [
|
||||
"traefik.http.routers.my-router.rule=Host(`example.com`)",
|
||||
]
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
## Routing Configuration
|
||||
|
||||
See the dedicated section in [routing](../routing/providers/nomad.md).
|
||||
|
||||
## Provider Configuration
|
||||
|
||||
### `refreshInterval`
|
||||
|
||||
_Optional, Default=15s_
|
||||
|
||||
Defines the polling interval.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
nomad:
|
||||
refreshInterval: 30s
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.nomad]
|
||||
refreshInterval = "30s"
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.nomad.refreshInterval=30s
|
||||
# ...
|
||||
```
|
||||
|
||||
### `prefix`
|
||||
|
||||
_required, Default="traefik"_
|
||||
|
||||
The prefix for Nomad service tags defining Traefik labels.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
nomad:
|
||||
prefix: test
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.nomad]
|
||||
prefix = "test"
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.nomad.prefix=test
|
||||
# ...
|
||||
```
|
||||
|
||||
### `stale`
|
||||
|
||||
_Optional, Default=false_
|
||||
|
||||
Use stale consistency for Nomad service API reads.
|
||||
|
||||
!!! note ""
|
||||
|
||||
This makes reads very fast and scalable at the cost of a higher likelihood of stale values.
|
||||
|
||||
For more information, see the Nomad [documentation on consistency](https://www.nomadproject.io/api-docs#consistency-modes).
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
nomad:
|
||||
stale: true
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.nomad]
|
||||
stale = true
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.nomad.stale=true
|
||||
# ...
|
||||
```
|
||||
|
||||
### `endpoint`
|
||||
|
||||
Defines the Nomad server endpoint.
|
||||
|
||||
#### `address`
|
||||
|
||||
Defines the address of the Nomad server.
|
||||
|
||||
_Optional, Default="http://127.0.0.1:4646"_
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
nomad:
|
||||
endpoint:
|
||||
address: http://127.0.0.1:4646
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.nomad]
|
||||
[providers.nomad.endpoint]
|
||||
address = "http://127.0.0.1:4646"
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.nomad.endpoint.address=http://127.0.0.1:4646
|
||||
# ...
|
||||
```
|
||||
|
||||
#### `datacenter`
|
||||
|
||||
_Optional, Default=""_
|
||||
|
||||
Defines the datacenter to use.
|
||||
If not provided in Traefik, Nomad uses the agent datacenter.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
nomad:
|
||||
endpoint:
|
||||
datacenter: dc1
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.nomad]
|
||||
[providers.nomad.endpoint]
|
||||
datacenter = "dc1"
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.nomad.endpoint.datacenter=dc1
|
||||
# ...
|
||||
```
|
||||
|
||||
#### `token`
|
||||
|
||||
_Optional, Default=""_
|
||||
|
||||
Token is used to provide a per-request ACL token, if Nomad ACLs are enabled.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
nomad:
|
||||
endpoint:
|
||||
token: test
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.nomad]
|
||||
[providers.nomad.endpoint]
|
||||
token = "test"
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.nomad.endpoint.token=test
|
||||
# ...
|
||||
```
|
||||
|
||||
#### `endpointWaitTime`
|
||||
|
||||
_Optional, Default=""_
|
||||
|
||||
Limits the duration for which a Watch can block.
|
||||
If not provided, the agent default values will be used.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
nomad:
|
||||
endpoint:
|
||||
endpointWaitTime: 15s
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.nomad]
|
||||
[providers.nomad.endpoint]
|
||||
endpointWaitTime = "15s"
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.nomad.endpoint.endpointwaittime=15s
|
||||
# ...
|
||||
```
|
||||
|
||||
#### `httpAuth`
|
||||
|
||||
_Optional_
|
||||
|
||||
Used to authenticate the HTTP client using HTTP Basic Authentication.
|
||||
|
||||
##### `username`
|
||||
|
||||
_Optional, Default=""_
|
||||
|
||||
Username to use for HTTP Basic Authentication.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
nomad:
|
||||
endpoint:
|
||||
httpAuth:
|
||||
username: admin
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.nomad.endpoint.httpAuth]
|
||||
username = "admin"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.nomad.endpoint.httpauth.username=admin
|
||||
```
|
||||
|
||||
##### `password`
|
||||
|
||||
_Optional, Default=""_
|
||||
|
||||
Password to use for HTTP Basic Authentication.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
nomad:
|
||||
endpoint:
|
||||
httpAuth:
|
||||
password: passw0rd
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.nomad.endpoint.httpAuth]
|
||||
password = "passw0rd"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.nomad.endpoint.httpauth.password=passw0rd
|
||||
```
|
||||
|
||||
#### `tls`
|
||||
|
||||
_Optional_
|
||||
|
||||
Defines the TLS configuration used for the secure connection to the Nomad API.
|
||||
|
||||
##### `ca`
|
||||
|
||||
_Optional_
|
||||
|
||||
`ca` is the path to the certificate authority used for the secure connection to Nomad,
|
||||
it defaults to the system bundle.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
nomad:
|
||||
endpoint:
|
||||
tls:
|
||||
ca: path/to/ca.crt
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.nomad.endpoint.tls]
|
||||
ca = "path/to/ca.crt"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.nomad.endpoint.tls.ca=path/to/ca.crt
|
||||
```
|
||||
|
||||
##### `cert`
|
||||
|
||||
_Optional_
|
||||
|
||||
`cert` is the path to the public certificate used for the secure connection to the Nomad API.
|
||||
When using this option, setting the `key` option is required.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
nomad:
|
||||
endpoint:
|
||||
tls:
|
||||
cert: path/to/foo.cert
|
||||
key: path/to/foo.key
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.nomad.endpoint.tls]
|
||||
cert = "path/to/foo.cert"
|
||||
key = "path/to/foo.key"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.nomad.endpoint.tls.cert=path/to/foo.cert
|
||||
--providers.nomad.endpoint.tls.key=path/to/foo.key
|
||||
```
|
||||
|
||||
##### `key`
|
||||
|
||||
_Optional_
|
||||
|
||||
`key` is the path to the private key used for the secure connection to the Nomad API.
|
||||
When using this option, setting the `cert` option is required.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
nomad:
|
||||
endpoint:
|
||||
tls:
|
||||
cert: path/to/foo.cert
|
||||
key: path/to/foo.key
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.nomad.endpoint.tls]
|
||||
cert = "path/to/foo.cert"
|
||||
key = "path/to/foo.key"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.nomad.endpoint.tls.cert=path/to/foo.cert
|
||||
--providers.nomad.endpoint.tls.key=path/to/foo.key
|
||||
```
|
||||
|
||||
##### `insecureSkipVerify`
|
||||
|
||||
_Optional, Default=false_
|
||||
|
||||
If `insecureSkipVerify` is `true`, the TLS connection to Nomad accepts any certificate presented by the server regardless of the hostnames it covers.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
nomad:
|
||||
endpoint:
|
||||
tls:
|
||||
insecureSkipVerify: true
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.nomad.endpoint.tls]
|
||||
insecureSkipVerify = true
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.nomad.endpoint.tls.insecureskipverify=true
|
||||
```
|
||||
|
||||
### `exposedByDefault`
|
||||
|
||||
_Optional, Default=true_
|
||||
|
||||
Expose Nomad services by default in Traefik.
|
||||
If set to `false`, services that do not have a `traefik.enable=true` tag will be ignored from the resulting routing configuration.
|
||||
|
||||
For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#restrict-the-scope-of-service-discovery).
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
nomad:
|
||||
exposedByDefault: false
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.nomad]
|
||||
exposedByDefault = false
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.nomad.exposedByDefault=false
|
||||
# ...
|
||||
```
|
||||
|
||||
### `defaultRule`
|
||||
|
||||
_Optional, Default=```Host(`{{ normalize .Name }}`)```_
|
||||
|
||||
The default host rule for all services.
|
||||
|
||||
For a given service, if no routing rule was defined by a tag, it is defined by this `defaultRule` instead.
|
||||
The `defaultRule` must be set to a valid [Go template](https://pkg.go.dev/text/template/),
|
||||
and can include [sprig template functions](https://masterminds.github.io/sprig/).
|
||||
The service name can be accessed with the `Name` identifier,
|
||||
and the template has access to all the labels (i.e. tags beginning with the `prefix`) defined on this service.
|
||||
|
||||
The option can be overridden on an instance basis with the `traefik.http.routers.{name-of-your-choice}.rule` tag.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
nomad:
|
||||
defaultRule: "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.nomad]
|
||||
defaultRule = "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.nomad.defaultRule="Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
### `constraints`
|
||||
|
||||
_Optional, Default=""_
|
||||
|
||||
The `constraints` option can be set to an expression that Traefik matches against the service tags to determine whether
|
||||
to create any route for that service. If none of the service tags match the expression, no route for that service is
|
||||
created. If the expression is empty, all detected services are included.
|
||||
|
||||
The expression syntax is based on the ```Tag(`tag`)```, and ```TagRegex(`tag`)``` functions,
|
||||
as well as the usual boolean logic, as shown in examples below.
|
||||
|
||||
??? example "Constraints Expression Examples"
|
||||
|
||||
```toml
|
||||
# Includes only services having the tag `a.tag.name=foo`
|
||||
constraints = "Tag(`a.tag.name=foo`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# Excludes services having any tag `a.tag.name=foo`
|
||||
constraints = "!Tag(`a.tag.name=foo`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical AND.
|
||||
constraints = "Tag(`a.tag.name`) && Tag(`another.tag.name`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical OR.
|
||||
constraints = "Tag(`a.tag.name`) || Tag(`another.tag.name`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical AND and OR, with precedence set by parentheses.
|
||||
constraints = "Tag(`a.tag.name`) && (Tag(`another.tag.name`) || Tag(`yet.another.tag.name`))"
|
||||
```
|
||||
|
||||
```toml
|
||||
# Includes only services having a tag matching the `a\.tag\.t.+` regular expression.
|
||||
constraints = "TagRegex(`a\.tag\.t.+`)"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
nomad:
|
||||
constraints: "Tag(`a.tag.name`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.nomad]
|
||||
constraints = "Tag(`a.tag.name`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.nomad.constraints="Tag(`a.tag.name`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#restrict-the-scope-of-service-discovery).
|
||||
|
||||
### `namespace`
|
||||
|
||||
_Optional, Default=""_
|
||||
|
||||
The `namespace` option defines the namespace in which the Nomad services will be discovered.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
nomad:
|
||||
namespace: "production"
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.nomad]
|
||||
namespace = "production"
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.nomad.namespace=production
|
||||
# ...
|
||||
```
|
|
@ -139,6 +139,7 @@ Below is the list of the currently supported providers in Traefik.
|
|||
| [Kubernetes Ingress](./kubernetes-ingress.md) | Orchestrator | Ingress | `kubernetes` |
|
||||
| [Kubernetes Gateway API](./kubernetes-gateway.md) | Orchestrator | Gateway API Resource | `kubernetesgateway` |
|
||||
| [Consul Catalog](./consul-catalog.md) | Orchestrator | Label | `consulcatalog` |
|
||||
| [Nomad](./nomad.md) | Orchestrator | Label | `nomad` |
|
||||
| [ECS](./ecs.md) | Orchestrator | Label | `ecs` |
|
||||
| [Marathon](./marathon.md) | Orchestrator | Label | `marathon` |
|
||||
| [Rancher](./rancher.md) | Orchestrator | Label | `rancher` |
|
||||
|
@ -213,6 +214,7 @@ List of providers that support these features:
|
|||
|
||||
- [Docker](./docker.md#exposedbydefault)
|
||||
- [Consul Catalog](./consul-catalog.md#exposedbydefault)
|
||||
- [Nomad](./nomad.md#exposedbydefault)
|
||||
- [Rancher](./rancher.md#exposedbydefault)
|
||||
- [Marathon](./marathon.md#exposedbydefault)
|
||||
|
||||
|
@ -222,6 +224,7 @@ List of providers that support constraints:
|
|||
|
||||
- [Docker](./docker.md#constraints)
|
||||
- [Consul Catalog](./consul-catalog.md#constraints)
|
||||
- [Nomad](./nomad.md#constraints)
|
||||
- [Rancher](./rancher.md#constraints)
|
||||
- [Marathon](./marathon.md#constraints)
|
||||
- [Kubernetes CRD](./kubernetes-crd.md#labelselector)
|
||||
|
|
16
docs/content/reference/dynamic-configuration/nomad.md
Normal file
16
docs/content/reference/dynamic-configuration/nomad.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
title: "Traefik Nomad Service Discovery Configuration Documentation"
|
||||
description: "View the reference for performing dynamic configurations with Traefik Proxy and Nomad Service Discovery. Read the technical documentation."
|
||||
---
|
||||
|
||||
# Nomad Service Discovery Configuration Reference
|
||||
|
||||
Dynamic configuration with Nomad Service Discovery
|
||||
{: .subtitle }
|
||||
|
||||
The labels are case insensitive.
|
||||
|
||||
```yaml
|
||||
--8<-- "content/reference/dynamic-configuration/nomad.yml"
|
||||
--8<-- "content/reference/dynamic-configuration/docker-labels.yml"
|
||||
```
|
1
docs/content/reference/dynamic-configuration/nomad.yml
Normal file
1
docs/content/reference/dynamic-configuration/nomad.yml
Normal file
|
@ -0,0 +1 @@
|
|||
- "traefik.enable=true"
|
|
@ -822,6 +822,57 @@ Display additional provider logs. (Default: ```false```)
|
|||
`--providers.marathon.watch`:
|
||||
Watch provider. (Default: ```true```)
|
||||
|
||||
`--providers.nomad`:
|
||||
Enable Nomad backend with default settings. (Default: ```false```)
|
||||
|
||||
`--providers.nomad.constraints`:
|
||||
Constraints is an expression that Traefik matches against the Nomad service's tags to determine whether to create route(s) for that service.
|
||||
|
||||
`--providers.nomad.defaultrule`:
|
||||
Default rule. (Default: ```Host(`{{ normalize .Name }}`)```)
|
||||
|
||||
`--providers.nomad.endpoint.address`:
|
||||
The address of the Nomad server, including scheme and port.
|
||||
|
||||
`--providers.nomad.endpoint.endpointwaittime`:
|
||||
WaitTime limits how long a Watch will block. If not provided, the agent default values will be used (Default: ```0```)
|
||||
|
||||
`--providers.nomad.endpoint.region`:
|
||||
Nomad region to use. If not provided, the local agent region is used.
|
||||
|
||||
`--providers.nomad.endpoint.tls.ca`:
|
||||
TLS CA
|
||||
|
||||
`--providers.nomad.endpoint.tls.caoptional`:
|
||||
TLS CA.Optional (Default: ```false```)
|
||||
|
||||
`--providers.nomad.endpoint.tls.cert`:
|
||||
TLS cert
|
||||
|
||||
`--providers.nomad.endpoint.tls.insecureskipverify`:
|
||||
TLS insecure skip verify (Default: ```false```)
|
||||
|
||||
`--providers.nomad.endpoint.tls.key`:
|
||||
TLS key
|
||||
|
||||
`--providers.nomad.endpoint.token`:
|
||||
Token is used to provide a per-request ACL token.
|
||||
|
||||
`--providers.nomad.exposedbydefault`:
|
||||
Expose Nomad services by default. (Default: ```true```)
|
||||
|
||||
`--providers.nomad.namespace`:
|
||||
Sets the Nomad namespace used to discover services.
|
||||
|
||||
`--providers.nomad.prefix`:
|
||||
Prefix for nomad service tags. (Default: ```traefik```)
|
||||
|
||||
`--providers.nomad.refreshinterval`:
|
||||
Interval for polling Nomad API. (Default: ```15```)
|
||||
|
||||
`--providers.nomad.stale`:
|
||||
Use stale consistency for catalog reads. (Default: ```false```)
|
||||
|
||||
`--providers.plugin.<name>`:
|
||||
Plugins configuration.
|
||||
|
||||
|
|
|
@ -822,6 +822,57 @@ Display additional provider logs. (Default: ```false```)
|
|||
`TRAEFIK_PROVIDERS_MARATHON_WATCH`:
|
||||
Watch provider. (Default: ```true```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_NOMAD`:
|
||||
Enable Nomad backend with default settings. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_NOMAD_CONSTRAINTS`:
|
||||
Constraints is an expression that Traefik matches against the Nomad service's tags to determine whether to create route(s) for that service.
|
||||
|
||||
`TRAEFIK_PROVIDERS_NOMAD_DEFAULTRULE`:
|
||||
Default rule. (Default: ```Host(`{{ normalize .Name }}`)```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_NOMAD_ENDPOINT_ADDRESS`:
|
||||
The address of the Nomad server, including scheme and port.
|
||||
|
||||
`TRAEFIK_PROVIDERS_NOMAD_ENDPOINT_ENDPOINTWAITTIME`:
|
||||
WaitTime limits how long a Watch will block. If not provided, the agent default values will be used (Default: ```0```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_NOMAD_ENDPOINT_REGION`:
|
||||
Nomad region to use. If not provided, the local agent region is used.
|
||||
|
||||
`TRAEFIK_PROVIDERS_NOMAD_ENDPOINT_TLS_CA`:
|
||||
TLS CA
|
||||
|
||||
`TRAEFIK_PROVIDERS_NOMAD_ENDPOINT_TLS_CAOPTIONAL`:
|
||||
TLS CA.Optional (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_NOMAD_ENDPOINT_TLS_CERT`:
|
||||
TLS cert
|
||||
|
||||
`TRAEFIK_PROVIDERS_NOMAD_ENDPOINT_TLS_INSECURESKIPVERIFY`:
|
||||
TLS insecure skip verify (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_NOMAD_ENDPOINT_TLS_KEY`:
|
||||
TLS key
|
||||
|
||||
`TRAEFIK_PROVIDERS_NOMAD_ENDPOINT_TOKEN`:
|
||||
Token is used to provide a per-request ACL token.
|
||||
|
||||
`TRAEFIK_PROVIDERS_NOMAD_EXPOSEDBYDEFAULT`:
|
||||
Expose Nomad services by default. (Default: ```true```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_NOMAD_NAMESPACE`:
|
||||
Sets the Nomad namespace used to discover services.
|
||||
|
||||
`TRAEFIK_PROVIDERS_NOMAD_PREFIX`:
|
||||
Prefix for nomad service tags. (Default: ```traefik```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_NOMAD_REFRESHINTERVAL`:
|
||||
Interval for polling Nomad API. (Default: ```15```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_NOMAD_STALE`:
|
||||
Use stale consistency for catalog reads. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_PLUGIN_<NAME>`:
|
||||
Plugins configuration.
|
||||
|
||||
|
|
|
@ -174,6 +174,25 @@
|
|||
[providers.consulCatalog.endpoint.httpAuth]
|
||||
username = "foobar"
|
||||
password = "foobar"
|
||||
[providers.nomad]
|
||||
constraints = "foobar"
|
||||
prefix = "foobar"
|
||||
refreshInterval = "42s"
|
||||
stale = true
|
||||
exposedByDefault = true
|
||||
defaultRule = "foobar"
|
||||
namespace = "foobar"
|
||||
[providers.nomad.endpoint]
|
||||
address = "foobar"
|
||||
region = "foobar"
|
||||
token = "foobar"
|
||||
endpointWaitTime = "42s"
|
||||
[providers.nomad.endpoint.tls]
|
||||
ca = "foobar"
|
||||
caOptional = true
|
||||
cert = "foobar"
|
||||
key = "foobar"
|
||||
insecureSkipVerify = true
|
||||
[providers.ecs]
|
||||
constraints = "foobar"
|
||||
exposedByDefault = true
|
||||
|
|
|
@ -188,6 +188,25 @@ providers:
|
|||
httpAuth:
|
||||
username: foobar
|
||||
password: foobar
|
||||
nomad:
|
||||
constraints: foobar
|
||||
prefix: foobar
|
||||
refreshInterval: 42s
|
||||
stale: true
|
||||
exposedByDefault: true
|
||||
defaultRule: foobar
|
||||
namespace: foobar
|
||||
endpoint:
|
||||
address: foobar
|
||||
region: foobar
|
||||
token: foobar
|
||||
endpointWaitTime: 42s
|
||||
tls:
|
||||
ca: foobar
|
||||
caOptional: true
|
||||
cert: foobar
|
||||
key: foobar
|
||||
insecureSkipVerify: true
|
||||
ecs:
|
||||
constraints: foobar
|
||||
exposedByDefault: true
|
||||
|
|
|
@ -8,7 +8,7 @@ description: "Learn how to use Consul Catalog as a provider for routing configur
|
|||
A Story of Tags, Services & Instances
|
||||
{: .subtitle }
|
||||
|
||||
![Rancher](../../assets/img/providers/consul.png)
|
||||
![Consul Catalog](../../assets/img/providers/consul.png)
|
||||
|
||||
Attach tags to your services and let Traefik do the rest!
|
||||
|
||||
|
|
466
docs/content/routing/providers/nomad.md
Normal file
466
docs/content/routing/providers/nomad.md
Normal file
|
@ -0,0 +1,466 @@
|
|||
---
|
||||
title: "Traefik Nomad Service Discovery Routing"
|
||||
description: "Learn how to use Nomad Service Discovery as a provider for routing configurations in Traefik Proxy. Read the technical documentation."
|
||||
---
|
||||
|
||||
# Traefik and Nomad Service Discovery
|
||||
|
||||
A story of Tags, Services & Nomads
|
||||
{: .subtitle }
|
||||
|
||||
![Nomad](../../assets/img/providers/nomad.png)
|
||||
|
||||
Attach tags to your Nomad services and let Traefik do the rest!
|
||||
|
||||
## Routing Configuration
|
||||
|
||||
!!! info "tags"
|
||||
|
||||
- tags are case insensitive.
|
||||
- The complete list of tags can be found [the reference page](../../reference/dynamic-configuration/nomad.md)
|
||||
|
||||
### General
|
||||
|
||||
Traefik creates, for each Nomad service, a corresponding Traefik [service](../services/index.md) and [router](../routers/index.md).
|
||||
|
||||
The Traefik service automatically gets a server per instance in this Nomad service, and the router gets a default rule attached to it, based on the Nomad service name.
|
||||
|
||||
### Routers
|
||||
|
||||
To update the configuration of the Router automatically attached to the service, add tags 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 tag ```traefik.http.routers.my-service.rule=Host(`example.com`)```.
|
||||
|
||||
??? info "`traefik.http.routers.<router_name>.rule`"
|
||||
|
||||
See [rule](../routers/index.md#rule) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.routers.myrouter.rule=Host(`example.com`)
|
||||
```
|
||||
|
||||
??? info "`traefik.http.routers.<router_name>.entrypoints`"
|
||||
|
||||
See [entry points](../routers/index.md#entrypoints) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.routers.myrouter.entrypoints=web,websecure
|
||||
```
|
||||
|
||||
??? info "`traefik.http.routers.<router_name>.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.<router_name>.service`"
|
||||
|
||||
See [rule](../routers/index.md#service) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.routers.myrouter.service=myservice
|
||||
```
|
||||
|
||||
??? info "`traefik.http.routers.<router_name>.tls`"
|
||||
|
||||
See [tls](../routers/index.md#tls) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.routers.myrouter.tls=true
|
||||
```
|
||||
|
||||
??? info "`traefik.http.routers.<router_name>.tls.certresolver`"
|
||||
|
||||
See [certResolver](../routers/index.md#certresolver) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.routers.myrouter.tls.certresolver=myresolver
|
||||
```
|
||||
|
||||
??? info "`traefik.http.routers.<router_name>.tls.domains[n].main`"
|
||||
|
||||
See [domains](../routers/index.md#domains) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.routers.myrouter.tls.domains[0].main=example.org
|
||||
```
|
||||
|
||||
??? info "`traefik.http.routers.<router_name>.tls.domains[n].sans`"
|
||||
|
||||
See [domains](../routers/index.md#domains) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.routers.myrouter.tls.domains[0].sans=test.example.org,dev.example.org
|
||||
```
|
||||
|
||||
??? info "`traefik.http.routers.<router_name>.tls.options`"
|
||||
|
||||
See [options](../routers/index.md#options) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.routers.myrouter.tls.options=foobar
|
||||
```
|
||||
|
||||
??? info "`traefik.http.routers.<router_name>.priority`"
|
||||
|
||||
See [priority](../routers/index.md#priority) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.routers.myrouter.priority=42
|
||||
```
|
||||
|
||||
### Services
|
||||
|
||||
To update the configuration of the Service automatically attached to the service,
|
||||
add tags 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 tag `traefik.http.services.{name-of-your-choice}.loadbalancer.passhostheader=false`.
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.server.port`"
|
||||
|
||||
Registers a port.
|
||||
Useful when the service exposes multiples ports.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.server.port=8080
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.server.scheme`"
|
||||
|
||||
Overrides the default scheme.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.server.scheme=http
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.serverstransport`"
|
||||
|
||||
Allows to reference a ServersTransport resource that is defined either with the File provider or the Kubernetes CRD one.
|
||||
See [serverstransport](../services/index.md#serverstransport) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.serverstransport=foobar@file
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.passhostheader`"
|
||||
|
||||
See [pass Host header](../services/index.md#pass-host-header) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.passhostheader=true
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.headers.<header_name>`"
|
||||
|
||||
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.<service_name>.loadbalancer.healthcheck.hostname`"
|
||||
|
||||
See [health check](../services/index.md#health-check) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.healthcheck.hostname=example.org
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.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.<service_name>.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.<service_name>.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.<service_name>.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.<service_name>.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.<service_name>.loadbalancer.healthcheck.followredirects`"
|
||||
|
||||
See [health check](../services/index.md#health-check) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.healthcheck.followredirects=true
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie`"
|
||||
|
||||
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.sticky.cookie=true
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.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.<service_name>.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.<service_name>.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.<service_name>.loadbalancer.sticky.cookie.samesite`"
|
||||
|
||||
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.sticky.cookie.samesite=none
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.responseforwarding.flushinterval`"
|
||||
|
||||
See [response forwarding](../services/index.md#response-forwarding) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.responseforwarding.flushinterval=10
|
||||
```
|
||||
|
||||
### Middleware
|
||||
|
||||
You can declare pieces of middleware using tags starting with `traefik.http.middlewares.{name-of-your-choice}.`, followed by the middleware type/options.
|
||||
|
||||
For example, to declare a middleware [`redirectscheme`](../../middlewares/http/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
|
||||
# ...
|
||||
# Declaring a middleware
|
||||
traefik.http.middlewares.my-redirect.redirectscheme.scheme=https
|
||||
# Referencing a middleware
|
||||
traefik.http.routers.my-service.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 tags.
|
||||
|
||||
??? example "Declaring TCP Routers and Services"
|
||||
|
||||
```yaml
|
||||
traefik.tcp.routers.my-router.rule=HostSNI(`example.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 Nomad service (but you have to do so manually).
|
||||
|
||||
#### TCP Routers
|
||||
|
||||
??? info "`traefik.tcp.routers.<router_name>.entrypoints`"
|
||||
|
||||
See [entry points](../routers/index.md#entrypoints_1) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.tcp.routers.mytcprouter.entrypoints=ep1,ep2
|
||||
```
|
||||
|
||||
??? info "`traefik.tcp.routers.<router_name>.rule`"
|
||||
|
||||
See [rule](../routers/index.md#rule_1) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.tcp.routers.mytcprouter.rule=HostSNI(`example.com`)
|
||||
```
|
||||
|
||||
??? info "`traefik.tcp.routers.<router_name>.service`"
|
||||
|
||||
See [service](../routers/index.md#services) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.tcp.routers.mytcprouter.service=myservice
|
||||
```
|
||||
|
||||
??? info "`traefik.tcp.routers.<router_name>.tls`"
|
||||
|
||||
See [TLS](../routers/index.md#tls_1) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.tcp.routers.mytcprouter.tls=true
|
||||
```
|
||||
|
||||
??? info "`traefik.tcp.routers.<router_name>.tls.certresolver`"
|
||||
|
||||
See [certResolver](../routers/index.md#certresolver_1) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.tcp.routers.mytcprouter.tls.certresolver=myresolver
|
||||
```
|
||||
|
||||
??? info "`traefik.tcp.routers.<router_name>.tls.domains[n].main`"
|
||||
|
||||
See [domains](../routers/index.md#domains_1) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.tcp.routers.mytcprouter.tls.domains[0].main=example.org
|
||||
```
|
||||
|
||||
??? info "`traefik.tcp.routers.<router_name>.tls.domains[n].sans`"
|
||||
|
||||
See [domains](../routers/index.md#domains_1) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.tcp.routers.mytcprouter.tls.domains[0].sans=test.example.org,dev.example.org
|
||||
```
|
||||
|
||||
??? info "`traefik.tcp.routers.<router_name>.tls.options`"
|
||||
|
||||
See [options](../routers/index.md#options_1) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.tcp.routers.mytcprouter.tls.options=myoptions
|
||||
```
|
||||
|
||||
??? info "`traefik.tcp.routers.<router_name>.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.<service_name>.loadbalancer.server.port`"
|
||||
|
||||
Registers a port of the application.
|
||||
|
||||
```yaml
|
||||
traefik.tcp.services.mytcpservice.loadbalancer.server.port=423
|
||||
```
|
||||
|
||||
??? info "`traefik.tcp.services.<service_name>.loadbalancer.terminationdelay`"
|
||||
|
||||
See [termination delay](../services/index.md#termination-delay) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.tcp.services.mytcpservice.loadbalancer.terminationdelay=100
|
||||
```
|
||||
|
||||
??? info "`traefik.tcp.services.<service_name>.loadbalancer.proxyprotocol.version`"
|
||||
|
||||
See [PROXY protocol](../services/index.md#proxy-protocol) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.tcp.services.mytcpservice.loadbalancer.proxyprotocol.version=1
|
||||
```
|
||||
|
||||
### UDP
|
||||
|
||||
You can declare UDP Routers and/or Services using tags.
|
||||
|
||||
??? example "Declaring UDP Routers and Services"
|
||||
|
||||
```yaml
|
||||
traefik.udp.routers.my-router.entrypoints=udp
|
||||
traefik.udp.services.my-service.loadbalancer.server.port=4123
|
||||
```
|
||||
|
||||
!!! warning "UDP and HTTP"
|
||||
|
||||
If you declare a UDP Router/Service, it will prevent Traefik from automatically creating an HTTP Router/Service (like it does by default if no UDP Router/Service is defined).
|
||||
You can declare both a UDP Router/Service and an HTTP Router/Service for the same Nomad service (but you have to do so manually).
|
||||
|
||||
#### UDP Routers
|
||||
|
||||
??? info "`traefik.udp.routers.<router_name>.entrypoints`"
|
||||
|
||||
See [entry points](../routers/index.md#entrypoints_2) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.udp.routers.myudprouter.entrypoints=ep1,ep2
|
||||
```
|
||||
|
||||
??? info "`traefik.udp.routers.<router_name>.service`"
|
||||
|
||||
See [service](../routers/index.md#services_1) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.udp.routers.myudprouter.service=myservice
|
||||
```
|
||||
|
||||
#### UDP Services
|
||||
|
||||
??? info "`traefik.udp.services.<service_name>.loadbalancer.server.port`"
|
||||
|
||||
Registers a port of the application.
|
||||
|
||||
```yaml
|
||||
traefik.udp.services.myudpservice.loadbalancer.server.port=423
|
||||
```
|
||||
|
||||
### Specific Provider Options
|
||||
|
||||
#### `traefik.enable`
|
||||
|
||||
```yaml
|
||||
traefik.enable=true
|
||||
```
|
||||
|
||||
You can tell Traefik to consider (or not) the service 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 Nomad Service Discovery flow.
|
||||
That means, if you just expose lets say port `:1337` on the Nomad job, traefik will pick up this port and use it.
|
|
@ -77,6 +77,7 @@ nav:
|
|||
- 'Kubernetes Ingress': 'providers/kubernetes-ingress.md'
|
||||
- 'Kubernetes Gateway API': 'providers/kubernetes-gateway.md'
|
||||
- 'Consul Catalog': 'providers/consul-catalog.md'
|
||||
- 'Nomad': 'providers/nomad.md'
|
||||
- 'ECS': 'providers/ecs.md'
|
||||
- 'Marathon': 'providers/marathon.md'
|
||||
- 'Rancher': 'providers/rancher.md'
|
||||
|
@ -97,6 +98,7 @@ nav:
|
|||
- 'Kubernetes Ingress': 'routing/providers/kubernetes-ingress.md'
|
||||
- 'Kubernetes Gateway API': 'routing/providers/kubernetes-gateway.md'
|
||||
- 'Consul Catalog': 'routing/providers/consul-catalog.md'
|
||||
- 'Nomad': 'routing/providers/nomad.md'
|
||||
- 'ECS': 'routing/providers/ecs.md'
|
||||
- 'Marathon': 'routing/providers/marathon.md'
|
||||
- 'Rancher': 'routing/providers/rancher.md'
|
||||
|
@ -194,6 +196,7 @@ nav:
|
|||
- 'Kubernetes CRD': 'reference/dynamic-configuration/kubernetes-crd.md'
|
||||
- 'Kubernetes Gateway API': 'reference/dynamic-configuration/kubernetes-gateway.md'
|
||||
- 'Consul Catalog': 'reference/dynamic-configuration/consul-catalog.md'
|
||||
- 'Nomad': "reference/dynamic-configuration/nomad.md"
|
||||
- 'ECS': 'reference/dynamic-configuration/ecs.md'
|
||||
- 'KV': 'reference/dynamic-configuration/kv.md'
|
||||
- 'Marathon': 'reference/dynamic-configuration/marathon.md'
|
||||
|
|
8
go.mod
8
go.mod
|
@ -31,6 +31,7 @@ require (
|
|||
github.com/hashicorp/go-hclog v0.16.1
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/hashicorp/go-version v1.3.0
|
||||
github.com/hashicorp/nomad/api v0.0.0-20220506174431-b5665129cd1f
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.7.0
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d
|
||||
github.com/instana/go-sensor v1.38.3
|
||||
|
@ -41,7 +42,7 @@ require (
|
|||
github.com/miekg/dns v1.1.47
|
||||
github.com/mitchellh/copystructure v1.0.0
|
||||
github.com/mitchellh/hashstructure v1.0.0
|
||||
github.com/mitchellh/mapstructure v1.4.2
|
||||
github.com/mitchellh/mapstructure v1.4.3
|
||||
github.com/opentracing/opentracing-go v1.2.0
|
||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5
|
||||
github.com/openzipkin/zipkin-go v0.2.2
|
||||
|
@ -176,8 +177,9 @@ require (
|
|||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
|
||||
github.com/hashicorp/consul/sdk v0.8.0 // indirect
|
||||
github.com/hashicorp/cronexpr v1.1.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-immutable-radix v1.3.0 // indirect
|
||||
github.com/hashicorp/go-msgpack v0.5.5 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
|
||||
|
@ -229,7 +231,7 @@ require (
|
|||
github.com/miekg/pkcs11 v1.0.3 // indirect
|
||||
github.com/mimuret/golang-iij-dpf v0.7.1 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.0 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.1 // indirect
|
||||
github.com/moby/buildkit v0.8.2-0.20210401015549-df49b648c8bf // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
|
|
17
go.sum
17
go.sum
|
@ -887,6 +887,8 @@ github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN
|
|||
github.com/hashicorp/consul/sdk v0.7.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM=
|
||||
github.com/hashicorp/consul/sdk v0.8.0 h1:OJtKBtEjboEZvG6AOUdh4Z1Zbyu0WcxQ0qatRrZHTVU=
|
||||
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
|
||||
github.com/hashicorp/cronexpr v1.1.1 h1:NJZDd87hGXjoZBdvyCF9mX4DCq5Wy7+A/w+A7q0wn6c=
|
||||
github.com/hashicorp/cronexpr v1.1.1/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4=
|
||||
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
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=
|
||||
|
@ -894,8 +896,9 @@ github.com/hashicorp/go-bexpr v0.1.2 h1:ijMXI4qERbzxbCnkxmfUtwMyjrrk3y+Vt0MxojNC
|
|||
github.com/hashicorp/go-bexpr v0.1.2/go.mod h1:ANbpTX1oAql27TZkKVeW8p1w8NTdnyzPe/0qqPCKohU=
|
||||
github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg=
|
||||
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-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-connlimit v0.3.0/go.mod h1:OUj9FGL1tPIhl/2RCfzYHrIiWj+VVPGNyVPnUX8AqS0=
|
||||
github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840/go.mod h1:Abjk0jbRkDaNCzsRhOv2iDCofYpX1eVsjozoiK63qLA=
|
||||
github.com/hashicorp/go-discover v0.0.0-20200501174627-ad1e96bde088/go.mod h1:vZu6Opqf49xX5lsFAu7iFNewkcVF1sn/wyapZh5ytlg=
|
||||
|
@ -963,6 +966,8 @@ github.com/hashicorp/memberlist v0.2.4/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOn
|
|||
github.com/hashicorp/memberlist v0.3.0 h1:8+567mCcFDnS5ADl7lrpxPMWiFCElyUEeW0gtj34fMA=
|
||||
github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
|
||||
github.com/hashicorp/net-rpc-msgpackrpc v0.0.0-20151116020338-a14192a58a69/go.mod h1:/z+jUGRBlwVpUZfjute9jWaF6/HuhjuFQuL1YXzVD1Q=
|
||||
github.com/hashicorp/nomad/api v0.0.0-20220506174431-b5665129cd1f h1:jSBbBJcPca465gK6XfwdXRQnFCd63e0oJmqllZTsawI=
|
||||
github.com/hashicorp/nomad/api v0.0.0-20220506174431-b5665129cd1f/go.mod h1:b/AoT79m3PEpb6tKCFKva/M+q1rKJNUk5mdu1S8DymM=
|
||||
github.com/hashicorp/raft v1.1.1/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8=
|
||||
github.com/hashicorp/raft v1.2.0/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8=
|
||||
github.com/hashicorp/raft v1.3.2 h1:j2tqHqFnDdWCepLxzuo3b6WzS2krIweBrvEoqBbWMTo=
|
||||
|
@ -1099,6 +1104,8 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
|
|||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
|
@ -1220,8 +1227,9 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG
|
|||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-testing-interface v1.14.0 h1:/x0XQ6h+3U3nAyk1yx+bHPURrKa9sVVvYbuqZ7pIAtI=
|
||||
github.com/mitchellh/go-testing-interface v1.14.0/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
|
||||
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0=
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
|
@ -1236,8 +1244,9 @@ github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
|
|||
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.1-0.20210112042008-8ebf2d61a8b4/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo=
|
||||
github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
|
||||
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
|
||||
github.com/mitchellh/pointerstructure v1.0.0 h1:ATSdz4NWrmWPOF1CeCBU4sMCno2hgqdbSrRPFWQSVZI=
|
||||
github.com/mitchellh/pointerstructure v1.0.0/go.mod h1:k4XwG94++jLVsSiTxo7qdIfXA9pj9EAeo0QsNNJOLZ8=
|
||||
|
@ -1510,6 +1519,8 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
|
|||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.4.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
"github.com/traefik/traefik/v2/pkg/provider/kv/redis"
|
||||
"github.com/traefik/traefik/v2/pkg/provider/kv/zk"
|
||||
"github.com/traefik/traefik/v2/pkg/provider/marathon"
|
||||
"github.com/traefik/traefik/v2/pkg/provider/nomad"
|
||||
"github.com/traefik/traefik/v2/pkg/provider/rancher"
|
||||
"github.com/traefik/traefik/v2/pkg/provider/rest"
|
||||
"github.com/traefik/traefik/v2/pkg/tls"
|
||||
|
@ -184,6 +185,7 @@ type Providers struct {
|
|||
Rest *rest.Provider `description:"Enable Rest backend with default settings." json:"rest,omitempty" toml:"rest,omitempty" yaml:"rest,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
Rancher *rancher.Provider `description:"Enable Rancher backend with default settings." json:"rancher,omitempty" toml:"rancher,omitempty" yaml:"rancher,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
|
||||
ConsulCatalog *consulcatalog.ProviderBuilder `description:"Enable ConsulCatalog backend with default settings." json:"consulCatalog,omitempty" toml:"consulCatalog,omitempty" yaml:"consulCatalog,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
Nomad *nomad.Provider `description:"Enable Nomad backend with default settings." json:"nomad,omitempty" toml:"nomad,omitempty" yaml:"nomad,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
Ecs *ecs.Provider `description:"Enable AWS ECS backend with default settings." json:"ecs,omitempty" toml:"ecs,omitempty" yaml:"ecs,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
|
||||
Consul *consul.ProviderBuilder `description:"Enable Consul backend with default settings." json:"consul,omitempty" toml:"consul,omitempty" yaml:"consul,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
|
|
|
@ -114,6 +114,10 @@ func NewProviderAggregator(conf static.Providers) ProviderAggregator {
|
|||
}
|
||||
}
|
||||
|
||||
if conf.Nomad != nil {
|
||||
p.quietAddProvider(conf.Nomad)
|
||||
}
|
||||
|
||||
if conf.Consul != nil {
|
||||
for _, pvd := range conf.Consul.BuildProviders() {
|
||||
p.quietAddProvider(pvd)
|
||||
|
|
267
pkg/provider/nomad/config.go
Normal file
267
pkg/provider/nomad/config.go
Normal file
|
@ -0,0 +1,267 @@
|
|||
package nomad
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/config/label"
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
"github.com/traefik/traefik/v2/pkg/provider"
|
||||
"github.com/traefik/traefik/v2/pkg/provider/constraints"
|
||||
)
|
||||
|
||||
func (p *Provider) buildConfig(ctx context.Context, items []item) *dynamic.Configuration {
|
||||
configurations := make(map[string]*dynamic.Configuration)
|
||||
|
||||
for _, i := range items {
|
||||
svcName := provider.Normalize(i.Node + "-" + i.Name + "-" + i.ID)
|
||||
ctxSvc := log.With(ctx, log.Str(log.ServiceName, svcName))
|
||||
|
||||
if !p.keepItem(ctxSvc, i) {
|
||||
continue
|
||||
}
|
||||
|
||||
logger := log.FromContext(ctx)
|
||||
labels := tagsToLabels(i.Tags, p.Prefix)
|
||||
|
||||
config, err := label.DecodeConfiguration(labels)
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to decode configuration: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
var tcpOrUDP bool
|
||||
|
||||
if len(config.TCP.Routers) > 0 || len(config.TCP.Services) > 0 {
|
||||
tcpOrUDP = true
|
||||
if err := p.buildTCPConfig(i, config.TCP); err != nil {
|
||||
logger.Errorf("Failed to build TCP service configuration: %v", err)
|
||||
continue
|
||||
}
|
||||
provider.BuildTCPRouterConfiguration(ctxSvc, config.TCP)
|
||||
}
|
||||
|
||||
if len(config.UDP.Routers) > 0 || len(config.UDP.Services) > 0 {
|
||||
tcpOrUDP = true
|
||||
if err := p.buildUDPConfig(i, config.UDP); err != nil {
|
||||
logger.Errorf("Failed to build UDP service configuration: %v", err)
|
||||
continue
|
||||
}
|
||||
provider.BuildUDPRouterConfiguration(ctxSvc, config.UDP)
|
||||
}
|
||||
|
||||
// tcp/udp, skip configuring http service
|
||||
if tcpOrUDP && len(config.HTTP.Routers) == 0 &&
|
||||
len(config.HTTP.Middlewares) == 0 &&
|
||||
len(config.HTTP.Services) == 0 {
|
||||
configurations[svcName] = config
|
||||
continue
|
||||
}
|
||||
|
||||
// configure http service
|
||||
if err := p.buildServiceConfig(i, config.HTTP); err != nil {
|
||||
logger.Errorf("Failed to build HTTP service configuration: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
model := struct {
|
||||
Name string
|
||||
Labels map[string]string
|
||||
}{
|
||||
Name: i.Name,
|
||||
Labels: labels,
|
||||
}
|
||||
|
||||
provider.BuildRouterConfiguration(ctx, config.HTTP, provider.Normalize(i.Name), p.defaultRuleTpl, model)
|
||||
configurations[svcName] = config
|
||||
}
|
||||
|
||||
return provider.Merge(ctx, configurations)
|
||||
}
|
||||
|
||||
func (p *Provider) buildTCPConfig(i item, configuration *dynamic.TCPConfiguration) error {
|
||||
if len(configuration.Services) == 0 {
|
||||
configuration.Services = make(map[string]*dynamic.TCPService)
|
||||
|
||||
lb := new(dynamic.TCPServersLoadBalancer)
|
||||
lb.SetDefaults()
|
||||
|
||||
configuration.Services[provider.Normalize(i.Name)] = &dynamic.TCPService{
|
||||
LoadBalancer: lb,
|
||||
}
|
||||
}
|
||||
|
||||
for _, service := range configuration.Services {
|
||||
if err := p.addServerTCP(i, service.LoadBalancer); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provider) buildUDPConfig(i item, configuration *dynamic.UDPConfiguration) error {
|
||||
if len(configuration.Services) == 0 {
|
||||
configuration.Services = make(map[string]*dynamic.UDPService)
|
||||
|
||||
configuration.Services[provider.Normalize(i.Name)] = &dynamic.UDPService{
|
||||
LoadBalancer: new(dynamic.UDPServersLoadBalancer),
|
||||
}
|
||||
}
|
||||
|
||||
for _, service := range configuration.Services {
|
||||
if err := p.addServerUDP(i, service.LoadBalancer); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provider) buildServiceConfig(i item, configuration *dynamic.HTTPConfiguration) error {
|
||||
if len(configuration.Services) == 0 {
|
||||
configuration.Services = make(map[string]*dynamic.Service)
|
||||
|
||||
lb := new(dynamic.ServersLoadBalancer)
|
||||
lb.SetDefaults()
|
||||
|
||||
configuration.Services[provider.Normalize(i.Name)] = &dynamic.Service{
|
||||
LoadBalancer: lb,
|
||||
}
|
||||
}
|
||||
|
||||
for _, service := range configuration.Services {
|
||||
if err := p.addServer(i, service.LoadBalancer); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: check whether it is mandatory to filter again.
|
||||
func (p *Provider) keepItem(ctx context.Context, i item) bool {
|
||||
logger := log.FromContext(ctx)
|
||||
|
||||
if !i.ExtraConf.Enable {
|
||||
logger.Debug("Filtering disabled item")
|
||||
return false
|
||||
}
|
||||
|
||||
matches, err := constraints.MatchTags(i.Tags, p.Constraints)
|
||||
if err != nil {
|
||||
logger.Errorf("Error matching constraint expressions: %v", err)
|
||||
return false
|
||||
}
|
||||
if !matches {
|
||||
logger.Debugf("Filtering out item due to constraints: %q", p.Constraints)
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO: filter on health when that information exists (nomad 1.4+)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *Provider) addServerTCP(i item, lb *dynamic.TCPServersLoadBalancer) error {
|
||||
if lb == nil {
|
||||
return errors.New("load-balancer is missing")
|
||||
}
|
||||
|
||||
var port string
|
||||
if len(lb.Servers) > 0 {
|
||||
port = lb.Servers[0].Port
|
||||
}
|
||||
|
||||
if len(lb.Servers) == 0 {
|
||||
lb.Servers = []dynamic.TCPServer{{}}
|
||||
}
|
||||
|
||||
if i.Port != 0 && port == "" {
|
||||
port = strconv.Itoa(i.Port)
|
||||
}
|
||||
lb.Servers[0].Port = ""
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
if i.Address == "" {
|
||||
return errors.New("address is missing")
|
||||
}
|
||||
|
||||
lb.Servers[0].Address = net.JoinHostPort(i.Address, port)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provider) addServerUDP(i item, lb *dynamic.UDPServersLoadBalancer) error {
|
||||
if lb == nil {
|
||||
return errors.New("load-balancer is missing")
|
||||
}
|
||||
|
||||
var port string
|
||||
if len(lb.Servers) > 0 {
|
||||
port = lb.Servers[0].Port
|
||||
}
|
||||
|
||||
if len(lb.Servers) == 0 {
|
||||
lb.Servers = []dynamic.UDPServer{{}}
|
||||
}
|
||||
|
||||
if i.Port != 0 && port == "" {
|
||||
port = strconv.Itoa(i.Port)
|
||||
}
|
||||
lb.Servers[0].Port = ""
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
if i.Address == "" {
|
||||
return errors.New("address is missing")
|
||||
}
|
||||
|
||||
lb.Servers[0].Address = net.JoinHostPort(i.Address, port)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provider) addServer(i item, lb *dynamic.ServersLoadBalancer) error {
|
||||
if lb == nil {
|
||||
return errors.New("load-balancer is missing")
|
||||
}
|
||||
|
||||
var port string
|
||||
if len(lb.Servers) > 0 {
|
||||
port = lb.Servers[0].Port
|
||||
}
|
||||
|
||||
if len(lb.Servers) == 0 {
|
||||
server := dynamic.Server{}
|
||||
server.SetDefaults()
|
||||
|
||||
lb.Servers = []dynamic.Server{server}
|
||||
}
|
||||
|
||||
if i.Port != 0 && port == "" {
|
||||
port = strconv.Itoa(i.Port)
|
||||
}
|
||||
lb.Servers[0].Port = ""
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
if i.Address == "" {
|
||||
return errors.New("address is missing")
|
||||
}
|
||||
|
||||
scheme := lb.Servers[0].Scheme
|
||||
lb.Servers[0].Scheme = ""
|
||||
lb.Servers[0].URL = fmt.Sprintf("%s://%s", scheme, net.JoinHostPort(i.Address, port))
|
||||
|
||||
return nil
|
||||
}
|
2280
pkg/provider/nomad/config_test.go
Normal file
2280
pkg/provider/nomad/config_test.go
Normal file
File diff suppressed because it is too large
Load diff
278
pkg/provider/nomad/nomad.go
Normal file
278
pkg/provider/nomad/nomad.go
Normal file
|
@ -0,0 +1,278 @@
|
|||
package nomad
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
"github.com/hashicorp/nomad/api"
|
||||
ptypes "github.com/traefik/paerser/types"
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/job"
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
"github.com/traefik/traefik/v2/pkg/provider"
|
||||
"github.com/traefik/traefik/v2/pkg/provider/constraints"
|
||||
"github.com/traefik/traefik/v2/pkg/safe"
|
||||
"github.com/traefik/traefik/v2/pkg/types"
|
||||
)
|
||||
|
||||
const (
|
||||
// providerName is the name of this provider.
|
||||
providerName = "nomad"
|
||||
|
||||
// defaultTemplateRule is the default template for the default rule.
|
||||
defaultTemplateRule = "Host(`{{ normalize .Name }}`)"
|
||||
|
||||
// defaultPrefix is the default prefix used in tag values indicating the service
|
||||
// should be consumed and exposed via traefik.
|
||||
defaultPrefix = "traefik"
|
||||
)
|
||||
|
||||
var _ provider.Provider = (*Provider)(nil)
|
||||
|
||||
type item struct {
|
||||
ID string // service ID
|
||||
Name string // service name
|
||||
Namespace string // service namespace
|
||||
Node string // node ID
|
||||
Datacenter string // region
|
||||
Address string // service address
|
||||
Port int // service port
|
||||
Tags []string // service tags
|
||||
|
||||
ExtraConf configuration // global options
|
||||
}
|
||||
|
||||
// Provider holds configurations of the provider.
|
||||
type Provider struct {
|
||||
DefaultRule string `description:"Default rule." json:"defaultRule,omitempty" toml:"defaultRule,omitempty" yaml:"defaultRule,omitempty"`
|
||||
Constraints string `description:"Constraints is an expression that Traefik matches against the Nomad service's tags to determine whether to create route(s) for that service." json:"constraints,omitempty" toml:"constraints,omitempty" yaml:"constraints,omitempty" export:"true"`
|
||||
Endpoint *EndpointConfig `description:"Nomad endpoint settings" json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty" export:"true"`
|
||||
Prefix string `description:"Prefix for nomad service tags." json:"prefix,omitempty" toml:"prefix,omitempty" yaml:"prefix,omitempty" export:"true"`
|
||||
Stale bool `description:"Use stale consistency for catalog reads." json:"stale,omitempty" toml:"stale,omitempty" yaml:"stale,omitempty" export:"true"`
|
||||
Namespace string `description:"Sets the Nomad namespace used to discover services." json:"namespace,omitempty" toml:"namespace,omitempty" yaml:"namespace,omitempty" export:"true"`
|
||||
ExposedByDefault bool `description:"Expose Nomad services by default." json:"exposedByDefault,omitempty" toml:"exposedByDefault,omitempty" yaml:"exposedByDefault,omitempty" export:"true"`
|
||||
RefreshInterval ptypes.Duration `description:"Interval for polling Nomad API." json:"refreshInterval,omitempty" toml:"refreshInterval,omitempty" yaml:"refreshInterval,omitempty" export:"true"`
|
||||
|
||||
client *api.Client // client for Nomad API
|
||||
defaultRuleTpl *template.Template // default routing rule
|
||||
}
|
||||
|
||||
type EndpointConfig struct {
|
||||
// Address is the Nomad endpoint address, if empty it defaults to NOMAD_ADDR or "http://localhost:4646".
|
||||
Address string `description:"The address of the Nomad server, including scheme and port." json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
|
||||
// Region is the Nomad region, if empty it defaults to NOMAD_REGION or "global".
|
||||
Region string `description:"Nomad region to use. If not provided, the local agent region is used." json:"region,omitempty" toml:"region,omitempty" yaml:"region,omitempty"`
|
||||
// Token is the ACL token to connect with Nomad, if empty it defaults to NOMAD_TOKEN.
|
||||
Token string `description:"Token is used to provide a per-request ACL token." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" loggable:"false"`
|
||||
TLS *types.ClientTLS `description:"Configure TLS." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
|
||||
EndpointWaitTime ptypes.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 for the Nomad Traefik Provider.
|
||||
func (p *Provider) SetDefaults() {
|
||||
p.Endpoint = &EndpointConfig{}
|
||||
p.Prefix = defaultPrefix
|
||||
p.ExposedByDefault = true
|
||||
p.RefreshInterval = ptypes.Duration(15 * time.Second)
|
||||
p.DefaultRule = defaultTemplateRule
|
||||
}
|
||||
|
||||
// Init the Nomad Traefik Provider.
|
||||
func (p *Provider) Init() error {
|
||||
defaultRuleTpl, err := provider.MakeDefaultRuleTemplate(p.DefaultRule, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while parsing default rule: %w", err)
|
||||
}
|
||||
p.defaultRuleTpl = defaultRuleTpl
|
||||
return nil
|
||||
}
|
||||
|
||||
// Provide allows the Nomad Traefik Provider to provide configurations to traefik
|
||||
// using the given configuration channel.
|
||||
func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error {
|
||||
var err error
|
||||
p.client, err = createClient(p.Namespace, p.Endpoint)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create nomad API client: %w", err)
|
||||
}
|
||||
|
||||
pool.GoCtx(func(routineCtx context.Context) {
|
||||
ctxLog := log.With(routineCtx, log.Str(log.ProviderName, providerName))
|
||||
logger := log.FromContext(ctxLog)
|
||||
|
||||
operation := func() error {
|
||||
ctx, cancel := context.WithCancel(ctxLog)
|
||||
defer cancel()
|
||||
|
||||
// load initial configuration
|
||||
if err := p.loadConfiguration(ctx, configurationChan); err != nil {
|
||||
return fmt.Errorf("failed to load initial nomad services: %w", err)
|
||||
}
|
||||
|
||||
// issue periodic refreshes in the background
|
||||
// (Nomad does not support Watch style observations)
|
||||
ticker := time.NewTicker(time.Duration(p.RefreshInterval))
|
||||
defer ticker.Stop()
|
||||
|
||||
// enter loop where we wait for and respond to notifications
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-ticker.C:
|
||||
}
|
||||
// load services due to refresh
|
||||
if err := p.loadConfiguration(ctx, configurationChan); err != nil {
|
||||
return fmt.Errorf("failed to refresh nomad services: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
failure := func(err error, d time.Duration) {
|
||||
logger.Errorf("Provider connection error %+v, retrying in %s", err, d)
|
||||
}
|
||||
|
||||
if retryErr := backoff.RetryNotify(
|
||||
safe.OperationWithRecover(operation),
|
||||
backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), ctxLog),
|
||||
failure,
|
||||
); retryErr != nil {
|
||||
logger.Errorf("Cannot connect to Nomad server %+v", retryErr)
|
||||
}
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provider) loadConfiguration(ctx context.Context, configurationC chan<- dynamic.Message) error {
|
||||
items, err := p.getNomadServiceData(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
configurationC <- dynamic.Message{
|
||||
ProviderName: providerName,
|
||||
Configuration: p.buildConfig(ctx, items),
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createClient(namespace string, endpoint *EndpointConfig) (*api.Client, error) {
|
||||
config := api.Config{
|
||||
Address: endpoint.Address,
|
||||
Region: endpoint.Region,
|
||||
WaitTime: time.Duration(endpoint.EndpointWaitTime),
|
||||
Namespace: namespace,
|
||||
}
|
||||
|
||||
if endpoint.TLS != nil {
|
||||
config.TLSConfig = &api.TLSConfig{
|
||||
CACert: endpoint.TLS.CA,
|
||||
ClientCert: endpoint.TLS.Cert,
|
||||
ClientKey: endpoint.TLS.Key,
|
||||
Insecure: endpoint.TLS.InsecureSkipVerify,
|
||||
}
|
||||
}
|
||||
|
||||
return api.NewClient(&config)
|
||||
}
|
||||
|
||||
// configuration contains information from the service's tags that are globals
|
||||
// (not specific to the dynamic configuration).
|
||||
type configuration struct {
|
||||
Enable bool // <prefix>.enable
|
||||
}
|
||||
|
||||
// globalConfig returns a configuration with settings not specific to the dynamic configuration (i.e. "<prefix>.enable").
|
||||
func (p *Provider) globalConfig(tags []string) configuration {
|
||||
enabled := p.ExposedByDefault
|
||||
labels := tagsToLabels(tags, p.Prefix)
|
||||
|
||||
if v, exists := labels["traefik.enable"]; exists {
|
||||
enabled = strings.EqualFold(v, "true")
|
||||
}
|
||||
|
||||
return configuration{Enable: enabled}
|
||||
}
|
||||
|
||||
func (p *Provider) getNomadServiceData(ctx context.Context) ([]item, error) {
|
||||
// first, get list of service stubs
|
||||
opts := &api.QueryOptions{AllowStale: p.Stale}
|
||||
opts = opts.WithContext(ctx)
|
||||
|
||||
stubs, _, err := p.client.Services().List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var items []item
|
||||
|
||||
for _, stub := range stubs {
|
||||
for _, service := range stub.Services {
|
||||
logger := log.FromContext(log.With(ctx, log.Str("serviceName", service.ServiceName)))
|
||||
|
||||
globalCfg := p.globalConfig(service.Tags)
|
||||
if !globalCfg.Enable {
|
||||
logger.Debug("Filter Nomad service that is not enabled")
|
||||
continue
|
||||
}
|
||||
|
||||
matches, err := constraints.MatchTags(service.Tags, p.Constraints)
|
||||
if err != nil {
|
||||
logger.Errorf("Error matching constraint expressions: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !matches {
|
||||
logger.Debugf("Filter Nomad service not matching constraints: %q", p.Constraints)
|
||||
continue
|
||||
}
|
||||
|
||||
instances, err := p.fetchService(ctx, service.ServiceName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, i := range instances {
|
||||
items = append(items, item{
|
||||
ID: i.ID,
|
||||
Name: i.ServiceName,
|
||||
Namespace: i.Namespace,
|
||||
Node: i.NodeID,
|
||||
Datacenter: i.Datacenter,
|
||||
Address: i.Address,
|
||||
Port: i.Port,
|
||||
Tags: i.Tags,
|
||||
ExtraConf: p.globalConfig(i.Tags),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
||||
|
||||
// fetchService queries Nomad API for services matching name,
|
||||
// that also have the <prefix>.enable=true set in its tags.
|
||||
func (p *Provider) fetchService(ctx context.Context, name string) ([]*api.ServiceRegistration, error) {
|
||||
var tagFilter string
|
||||
if !p.ExposedByDefault {
|
||||
tagFilter = fmt.Sprintf(`Tags contains %q`, fmt.Sprintf("%s.enable=true", p.Prefix))
|
||||
}
|
||||
|
||||
// TODO: Nomad currently (v1.3.0) does not support health checks,
|
||||
// and as such does not yet return health status information.
|
||||
// When it does, refactor this section to include health status.
|
||||
opts := &api.QueryOptions{AllowStale: p.Stale, Filter: tagFilter}
|
||||
opts = opts.WithContext(ctx)
|
||||
|
||||
services, _, err := p.client.Services().Get(name, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch services: %w", err)
|
||||
}
|
||||
return services, nil
|
||||
}
|
169
pkg/provider/nomad/nomad_test.go
Normal file
169
pkg/provider/nomad/nomad_test.go
Normal file
|
@ -0,0 +1,169 @@
|
|||
package nomad
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_globalConfig(t *testing.T) {
|
||||
cases := []struct {
|
||||
Name string
|
||||
Prefix string
|
||||
Tags []string
|
||||
ExposedByDefault bool
|
||||
exp configuration
|
||||
}{
|
||||
{
|
||||
Name: "expose_by_default_no_tags",
|
||||
Prefix: "traefik",
|
||||
Tags: nil,
|
||||
ExposedByDefault: true,
|
||||
exp: configuration{Enable: true},
|
||||
},
|
||||
{
|
||||
Name: "not_expose_by_default_no_tags",
|
||||
Prefix: "traefik",
|
||||
Tags: nil,
|
||||
ExposedByDefault: false,
|
||||
exp: configuration{Enable: false},
|
||||
},
|
||||
{
|
||||
Name: "expose_by_default_tags_enable",
|
||||
Prefix: "traefik",
|
||||
Tags: []string{"traefik.enable=true"},
|
||||
ExposedByDefault: true,
|
||||
exp: configuration{Enable: true},
|
||||
},
|
||||
{
|
||||
Name: "expose_by_default_tags_disable",
|
||||
Prefix: "traefik",
|
||||
Tags: []string{"traefik.enable=false"},
|
||||
ExposedByDefault: true,
|
||||
exp: configuration{Enable: false},
|
||||
},
|
||||
{
|
||||
Name: "expose_by_default_tags_enable_custom_prefix",
|
||||
Prefix: "custom",
|
||||
Tags: []string{"custom.enable=true"},
|
||||
ExposedByDefault: true,
|
||||
exp: configuration{Enable: true},
|
||||
},
|
||||
{
|
||||
Name: "expose_by_default_tags_disable_custom_prefix",
|
||||
Prefix: "custom",
|
||||
Tags: []string{"custom.enable=false"},
|
||||
ExposedByDefault: true,
|
||||
exp: configuration{Enable: false},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range cases {
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
p := Provider{ExposedByDefault: test.ExposedByDefault, Prefix: test.Prefix}
|
||||
result := p.globalConfig(test.Tags)
|
||||
require.Equal(t, test.exp, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getNomadServiceData(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch {
|
||||
case strings.HasSuffix(r.RequestURI, "/v1/services"):
|
||||
_, _ = w.Write([]byte(services))
|
||||
case strings.HasSuffix(r.RequestURI, "/v1/service/redis"):
|
||||
_, _ = w.Write([]byte(redis))
|
||||
case strings.HasSuffix(r.RequestURI, "/v1/service/hello-nomad"):
|
||||
_, _ = w.Write([]byte(hello))
|
||||
}
|
||||
}))
|
||||
t.Cleanup(ts.Close)
|
||||
|
||||
p := new(Provider)
|
||||
p.SetDefaults()
|
||||
p.Endpoint.Address = ts.URL
|
||||
err := p.Init()
|
||||
require.NoError(t, err)
|
||||
|
||||
// fudge client, avoid starting up via Provide
|
||||
p.client, err = createClient(p.Namespace, p.Endpoint)
|
||||
require.NoError(t, err)
|
||||
|
||||
// make the query for services
|
||||
items, err := p.getNomadServiceData(context.TODO())
|
||||
require.NoError(t, err)
|
||||
require.Len(t, items, 2)
|
||||
}
|
||||
|
||||
const services = `
|
||||
[
|
||||
{
|
||||
"Namespace": "default",
|
||||
"Services": [
|
||||
{
|
||||
"ServiceName": "redis",
|
||||
"Tags": [
|
||||
"traefik.enable=true"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ServiceName": "hello-nomad",
|
||||
"Tags": [
|
||||
"traefik.enable=true",
|
||||
"traefik.http.routers.hellon.entrypoints=web",
|
||||
"traefik.http.routers.hellon.service=hello-nomad"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
`
|
||||
|
||||
const redis = `
|
||||
[
|
||||
{
|
||||
"Address": "127.0.0.1",
|
||||
"AllocID": "07501480-8175-8071-7da6-133bd1ff890f",
|
||||
"CreateIndex": 46,
|
||||
"Datacenter": "dc1",
|
||||
"ID": "_nomad-task-07501480-8175-8071-7da6-133bd1ff890f-group-redis-redis-redis",
|
||||
"JobID": "echo",
|
||||
"ModifyIndex": 46,
|
||||
"Namespace": "default",
|
||||
"NodeID": "6d7f412e-e7ff-2e66-d47b-867b0e9d8726",
|
||||
"Port": 30826,
|
||||
"ServiceName": "redis",
|
||||
"Tags": [
|
||||
"traefik.enable=true"
|
||||
]
|
||||
}
|
||||
]
|
||||
`
|
||||
|
||||
const hello = `
|
||||
[
|
||||
{
|
||||
"Address": "127.0.0.1",
|
||||
"AllocID": "71a63a80-a98a-93ee-4fd7-73b808577c20",
|
||||
"CreateIndex": 18,
|
||||
"Datacenter": "dc1",
|
||||
"ID": "_nomad-task-71a63a80-a98a-93ee-4fd7-73b808577c20-group-hello-nomad-hello-nomad-http",
|
||||
"JobID": "echo",
|
||||
"ModifyIndex": 18,
|
||||
"Namespace": "default",
|
||||
"NodeID": "6d7f412e-e7ff-2e66-d47b-867b0e9d8726",
|
||||
"Port": 20627,
|
||||
"ServiceName": "hello-nomad",
|
||||
"Tags": [
|
||||
"traefik.enable=true",
|
||||
"traefik.http.routers.hellon.entrypoints=web",
|
||||
"traefik.http.routers.hellon.service=hello-nomad"
|
||||
]
|
||||
}
|
||||
]
|
||||
`
|
19
pkg/provider/nomad/tag.go
Normal file
19
pkg/provider/nomad/tag.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package nomad
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func tagsToLabels(tags []string, prefix string) map[string]string {
|
||||
labels := make(map[string]string, len(tags))
|
||||
for _, tag := range tags {
|
||||
if strings.HasPrefix(tag, prefix) {
|
||||
if parts := strings.SplitN(tag, "=", 2); len(parts) == 2 {
|
||||
left, right := strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])
|
||||
key := "traefik." + strings.TrimPrefix(left, prefix+".")
|
||||
labels[key] = right
|
||||
}
|
||||
}
|
||||
}
|
||||
return labels
|
||||
}
|
109
pkg/provider/nomad/tag_test.go
Normal file
109
pkg/provider/nomad/tag_test.go
Normal file
|
@ -0,0 +1,109 @@
|
|||
package nomad
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_tagsToLabels(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
tags []string
|
||||
prefix string
|
||||
expected map[string]string
|
||||
}{
|
||||
{
|
||||
desc: "no tags",
|
||||
tags: []string{},
|
||||
prefix: "traefik",
|
||||
expected: map[string]string{},
|
||||
},
|
||||
{
|
||||
desc: "minimal global config",
|
||||
tags: []string{"traefik.enable=false"},
|
||||
prefix: "traefik",
|
||||
expected: map[string]string{
|
||||
"traefik.enable": "false",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "config with domain",
|
||||
tags: []string{
|
||||
"traefik.enable=true",
|
||||
"traefik.domain=example.com",
|
||||
},
|
||||
prefix: "traefik",
|
||||
expected: map[string]string{
|
||||
"traefik.enable": "true",
|
||||
"traefik.domain": "example.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "config with custom prefix",
|
||||
tags: []string{
|
||||
"custom.enable=true",
|
||||
"custom.domain=example.com",
|
||||
},
|
||||
prefix: "custom",
|
||||
expected: map[string]string{
|
||||
"traefik.enable": "true",
|
||||
"traefik.domain": "example.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "config with spaces in tags",
|
||||
tags: []string{
|
||||
"custom.enable = true",
|
||||
"custom.domain = example.com",
|
||||
},
|
||||
prefix: "custom",
|
||||
expected: map[string]string{
|
||||
"traefik.enable": "true",
|
||||
"traefik.domain": "example.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
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 := tagsToLabels(test.tags, test.prefix)
|
||||
|
||||
assert.Equal(t, test.expected, labels)
|
||||
})
|
||||
}
|
||||
}
|
7
webui/src/statics/providers/nomad.svg
Executable file
7
webui/src/statics/providers/nomad.svg
Executable file
|
@ -0,0 +1,7 @@
|
|||
<svg id="LOGOS" xmlns="http://www.w3.org/2000/svg" width="68" height="68" viewBox="27 38 68 68">
|
||||
<defs>
|
||||
<style>.cls-1{fill:#00ca8e;}</style>
|
||||
</defs>
|
||||
<path class="cls-1"
|
||||
d="M61.14,38.19,32,55V88.63l29.12,16.81L90.26,88.63V55Zm13,37-7.76,4.48L57,74.54V85.26l-8.81,5.59V68.45l7-4.28,9.7,5.11V58.34l9.25-5.56Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 334 B |
Loading…
Reference in a new issue