Nomad provider to allow empty services

This commit is contained in:
chrispruitt 2024-04-04 05:54:04 -04:00 committed by GitHub
parent 2c6418e17a
commit ac1753a614
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
41 changed files with 3540 additions and 87 deletions

View file

@ -511,3 +511,27 @@ providers:
--providers.nomad.namespaces=ns1,ns2 --providers.nomad.namespaces=ns1,ns2
# ... # ...
``` ```
### `allowEmptyServices`
_Optional, Default: false_
If the parameter is set to `true`,
it allows the creation of an empty [servers load balancer](../routing/services/index.md#servers-load-balancer) if the targeted Nomad service has no endpoints available. This results in a `503` HTTP response instead of a `404`.
```yaml tab="File (YAML)"
providers:
nomad:
allowEmptyServices: true
# ...
```
```toml tab="File (TOML)"
[providers.nomad]
allowEmptyServices = true
# ...
```
```bash tab="CLI"
--providers.nomad.allowEmptyServices=true
```

View file

@ -789,6 +789,9 @@ Kubernetes bearer token (not needed for in-cluster client). It accepts either a
`--providers.nomad`: `--providers.nomad`:
Enable Nomad backend with default settings. (Default: ```false```) Enable Nomad backend with default settings. (Default: ```false```)
`--providers.nomad.allowemptyservices`:
Allow the creation of services without endpoints. (Default: ```false```)
`--providers.nomad.constraints`: `--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. Constraints is an expression that Traefik matches against the Nomad service's tags to determine whether to create route(s) for that service.

View file

@ -789,6 +789,9 @@ Kubernetes bearer token (not needed for in-cluster client). It accepts either a
`TRAEFIK_PROVIDERS_NOMAD`: `TRAEFIK_PROVIDERS_NOMAD`:
Enable Nomad backend with default settings. (Default: ```false```) Enable Nomad backend with default settings. (Default: ```false```)
`TRAEFIK_PROVIDERS_NOMAD_ALLOWEMPTYSERVICES`:
Allow the creation of services without endpoints. (Default: ```false```)
`TRAEFIK_PROVIDERS_NOMAD_CONSTRAINTS`: `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. Constraints is an expression that Traefik matches against the Nomad service's tags to determine whether to create route(s) for that service.

View file

@ -185,6 +185,7 @@
stale = true stale = true
exposedByDefault = true exposedByDefault = true
refreshInterval = "42s" refreshInterval = "42s"
allowEmptyServices = true
namespaces = ["foobar", "foobar"] namespaces = ["foobar", "foobar"]
[providers.nomad.endpoint] [providers.nomad.endpoint]
address = "foobar" address = "foobar"

View file

@ -216,6 +216,7 @@ providers:
stale: true stale: true
exposedByDefault: true exposedByDefault: true
refreshInterval: 42s refreshInterval: 42s
allowEmptyServices: true
namespaces: namespaces:
- foobar - foobar
- foobar - foobar

View file

@ -97,8 +97,11 @@ func (p *Provider) buildTCPConfig(i item, configuration *dynamic.TCPConfiguratio
} }
for _, service := range configuration.Services { for _, service := range configuration.Services {
if err := p.addServerTCP(i, service.LoadBalancer); err != nil { // Leave load balancer empty when no address and allowEmptyServices = true
return err if !(i.Address == "" && p.AllowEmptyServices) {
if err := p.addServerTCP(i, service.LoadBalancer); err != nil {
return err
}
} }
} }
@ -115,8 +118,11 @@ func (p *Provider) buildUDPConfig(i item, configuration *dynamic.UDPConfiguratio
} }
for _, service := range configuration.Services { for _, service := range configuration.Services {
if err := p.addServerUDP(i, service.LoadBalancer); err != nil { // Leave load balancer empty when no address and allowEmptyServices = true
return err if !(i.Address == "" && p.AllowEmptyServices) {
if err := p.addServerUDP(i, service.LoadBalancer); err != nil {
return err
}
} }
} }
@ -136,8 +142,11 @@ func (p *Provider) buildServiceConfig(i item, configuration *dynamic.HTTPConfigu
} }
for _, service := range configuration.Services { for _, service := range configuration.Services {
if err := p.addServer(i, service.LoadBalancer); err != nil { // Leave load balancer empty when no address and allowEmptyServices = true
return err if !(i.Address == "" && p.AllowEmptyServices) {
if err := p.addServer(i, service.LoadBalancer); err != nil {
return err
}
} }
} }

View file

@ -706,6 +706,42 @@ func Test_buildConfig(t *testing.T) {
}, },
}, },
}, },
{
desc: "empty service",
items: []item{
{
ID: "id1",
Name: "Test",
Tags: []string{
"traefik.enable=true",
},
Address: "",
Port: -1,
ExtraConf: configuration{Enable: true},
},
},
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Stores: map[string]tls.Store{},
},
},
},
{ {
desc: "one service with rule label", desc: "one service with rule label",
items: []item{ items: []item{
@ -2825,6 +2861,307 @@ func Test_buildConfig(t *testing.T) {
} }
} }
func Test_buildConfigAllowEmptyServicesTrue(t *testing.T) {
testCases := []struct {
desc string
items []item
constraints string
expected *dynamic.Configuration
}{
{
desc: "empty service http",
items: []item{
{
ID: "id1",
Name: "Test",
Tags: []string{
"traefik.enable=true",
},
Address: "",
Port: -1,
ExtraConf: configuration{Enable: true},
},
},
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"Test": {
Service: "Test",
Rule: "Host(`Test.traefik.test`)",
DefaultRule: true,
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"Test": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: nil,
PassHostHeader: Bool(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Stores: map[string]tls.Store{},
},
},
},
{
desc: "empty service tcp",
items: []item{
{
ID: "id1",
Name: "Test",
Tags: []string{
"traefik.tcp.routers.test.rule = HostSNI(`foobar`)",
},
Address: "",
Port: -1,
ExtraConf: configuration{Enable: true},
},
},
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"test": {
Rule: "HostSNI(`foobar`)",
Service: "Test",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"Test": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Stores: map[string]tls.Store{},
},
},
},
{
desc: "empty service udp",
items: []item{
{
ID: "id1",
Name: "Test",
Tags: []string{
"traefik.udp.routers.test.entrypoints = udp",
},
Address: "",
Port: -1,
ExtraConf: configuration{Enable: true},
},
},
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{
"test": {
EntryPoints: []string{"udp"},
Service: "Test",
},
},
Services: map[string]*dynamic.UDPService{
"Test": {
LoadBalancer: &dynamic.UDPServersLoadBalancer{},
},
},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Stores: map[string]tls.Store{},
},
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
p := new(Provider)
p.SetDefaults()
p.AllowEmptyServices = true
p.DefaultRule = "Host(`{{ normalize .Name }}.traefik.test`)"
p.Constraints = test.constraints
err := p.Init()
require.NoError(t, err)
ctx := context.TODO()
c := p.buildConfig(ctx, test.items)
require.Equal(t, test.expected, c)
})
}
}
func Test_buildConfigAllowEmptyServicesFalseDefault(t *testing.T) {
testCases := []struct {
desc string
items []item
constraints string
expected *dynamic.Configuration
}{
{
desc: "empty service http",
items: []item{
{
ID: "id1",
Name: "Test",
Tags: []string{
"traefik.enable=true",
},
Address: "",
Port: -1,
ExtraConf: configuration{Enable: true},
},
},
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Stores: map[string]tls.Store{},
},
},
},
{
desc: "empty service tcp",
items: []item{
{
ID: "id1",
Name: "Test",
Tags: []string{
"traefik.tcp.routers.test.rule = HostSNI(`foobar`)",
},
Address: "",
Port: -1,
ExtraConf: configuration{Enable: true},
},
},
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Stores: map[string]tls.Store{},
},
},
},
{
desc: "empty service udp",
items: []item{
{
ID: "id1",
Name: "Test",
Tags: []string{
"traefik.udp.routers.test.entrypoints = udp",
},
Address: "",
Port: -1,
ExtraConf: configuration{Enable: true},
},
},
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Stores: map[string]tls.Store{},
},
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
p := new(Provider)
p.SetDefaults()
p.DefaultRule = "Host(`{{ normalize .Name }}.traefik.test`)"
p.Constraints = test.constraints
err := p.Init()
require.NoError(t, err)
ctx := context.TODO()
c := p.buildConfig(ctx, test.items)
require.Equal(t, test.expected, c)
})
}
}
func Test_keepItem(t *testing.T) { func Test_keepItem(t *testing.T) {
testCases := []struct { testCases := []struct {
name string name string

View file

@ -0,0 +1,220 @@
{
"Stop": false,
"Region": "global",
"Namespace": "default",
"ID": "job1",
"ParentID": "",
"Name": "job1",
"Type": "service",
"Priority": 50,
"AllAtOnce": false,
"Datacenters": [
"dc1"
],
"NodePool": "default",
"Constraints": null,
"Affinities": null,
"Spreads": null,
"TaskGroups": [
{
"Name": "group1",
"Count": 1,
"Update": {
"Stagger": 30000000000,
"MaxParallel": 1,
"HealthCheck": "checks",
"MinHealthyTime": 10000000000,
"HealthyDeadline": 300000000000,
"ProgressDeadline": 600000000000,
"AutoRevert": false,
"AutoPromote": false,
"Canary": 0
},
"Migrate": {
"MaxParallel": 1,
"HealthCheck": "checks",
"MinHealthyTime": 10000000000,
"HealthyDeadline": 300000000000
},
"Constraints": [
{
"LTarget": "${attr.nomad.service_discovery}",
"RTarget": "true",
"Operand": "="
}
],
"Scaling": {
"ID": "654cb8ee-9c81-4fe8-02a0-2aecdea35ae3",
"Type": "horizontal",
"Target": {
"Job": "job1",
"Group": "group1",
"Namespace": "default"
},
"Policy": null,
"Min": 0,
"Max": 3,
"Enabled": true,
"CreateIndex": 0,
"ModifyIndex": 0
},
"RestartPolicy": {
"Attempts": 2,
"Interval": 1800000000000,
"Delay": 15000000000,
"Mode": "fail",
"RenderTemplates": false
},
"Tasks": [
{
"Name": "task1",
"Driver": "docker",
"User": "",
"Config": {
"image": "nginx",
"ports": [
"http"
]
},
"Env": null,
"Services": null,
"Vault": null,
"Templates": null,
"Constraints": null,
"Affinities": null,
"Resources": {
"CPU": 100,
"Cores": 0,
"MemoryMB": 300,
"MemoryMaxMB": 0,
"DiskMB": 0,
"IOPS": 0,
"Networks": null,
"Devices": null
},
"RestartPolicy": {
"Attempts": 2,
"Interval": 1800000000000,
"Delay": 15000000000,
"Mode": "fail",
"RenderTemplates": false
},
"DispatchPayload": null,
"Lifecycle": null,
"Meta": null,
"KillTimeout": 5000000000,
"LogConfig": {
"MaxFiles": 10,
"MaxFileSizeMB": 10,
"Disabled": false
},
"Artifacts": null,
"Leader": false,
"ShutdownDelay": 0,
"VolumeMounts": null,
"ScalingPolicies": null,
"KillSignal": "",
"Kind": "",
"CSIPluginConfig": null,
"Identity": null
}
],
"EphemeralDisk": {
"Sticky": false,
"SizeMB": 300,
"Migrate": false
},
"Meta": null,
"ReschedulePolicy": {
"Attempts": 0,
"Interval": 0,
"Delay": 30000000000,
"DelayFunction": "exponential",
"MaxDelay": 3600000000000,
"Unlimited": true
},
"Affinities": null,
"Spreads": null,
"Networks": [
{
"Mode": "",
"Device": "",
"CIDR": "",
"IP": "",
"Hostname": "",
"MBits": 0,
"DNS": null,
"ReservedPorts": null,
"DynamicPorts": [
{
"Label": "http",
"Value": 0,
"To": 80,
"HostNetwork": "default"
}
]
}
],
"Consul": {
"Namespace": ""
},
"Services": [
{
"Name": "job1",
"TaskName": "",
"PortLabel": "http",
"AddressMode": "auto",
"Address": "",
"EnableTagOverride": false,
"Tags": [
"traefik.enable=true"
],
"CanaryTags": null,
"Checks": null,
"Connect": null,
"Meta": null,
"CanaryMeta": null,
"TaggedAddresses": null,
"Namespace": "default",
"OnUpdate": "require_healthy",
"Provider": "nomad"
}
],
"Volumes": null,
"ShutdownDelay": null,
"StopAfterClientDisconnect": null,
"MaxClientDisconnect": null
}
],
"Update": {
"Stagger": 30000000000,
"MaxParallel": 1,
"HealthCheck": "",
"MinHealthyTime": 0,
"HealthyDeadline": 0,
"ProgressDeadline": 0,
"AutoRevert": false,
"AutoPromote": false,
"Canary": 0
},
"Multiregion": null,
"Periodic": null,
"ParameterizedJob": null,
"Dispatched": false,
"DispatchIdempotencyToken": "",
"Payload": null,
"Meta": null,
"ConsulToken": "",
"ConsulNamespace": "",
"VaultToken": "",
"VaultNamespace": "",
"NomadTokenID": "",
"Status": "running",
"StatusDescription": "",
"Stable": true,
"Version": 11,
"SubmitTime": 1705690395733241600,
"CreateIndex": 493,
"ModifyIndex": 9961,
"JobModifyIndex": 9955
}

View file

@ -0,0 +1,220 @@
{
"Stop": false,
"Region": "global",
"Namespace": "default",
"ID": "job2",
"ParentID": "",
"Name": "job2",
"Type": "service",
"Priority": 50,
"AllAtOnce": false,
"Datacenters": [
"dc1"
],
"NodePool": "default",
"Constraints": null,
"Affinities": null,
"Spreads": null,
"TaskGroups": [
{
"Name": "group1",
"Count": 0,
"Update": {
"Stagger": 30000000000,
"MaxParallel": 1,
"HealthCheck": "checks",
"MinHealthyTime": 10000000000,
"HealthyDeadline": 300000000000,
"ProgressDeadline": 600000000000,
"AutoRevert": false,
"AutoPromote": false,
"Canary": 0
},
"Migrate": {
"MaxParallel": 1,
"HealthCheck": "checks",
"MinHealthyTime": 10000000000,
"HealthyDeadline": 300000000000
},
"Constraints": [
{
"LTarget": "${attr.nomad.service_discovery}",
"RTarget": "true",
"Operand": "="
}
],
"Scaling": {
"ID": "0ae1d8fa-aa84-0b1b-941f-82dc71bc4664",
"Type": "horizontal",
"Target": {
"Group": "group1",
"Namespace": "default",
"Job": "job2"
},
"Policy": null,
"Min": 0,
"Max": 3,
"Enabled": true,
"CreateIndex": 9975,
"ModifyIndex": 9975
},
"RestartPolicy": {
"Attempts": 2,
"Interval": 1800000000000,
"Delay": 15000000000,
"Mode": "fail",
"RenderTemplates": false
},
"Tasks": [
{
"Name": "task1",
"Driver": "docker",
"User": "",
"Config": {
"ports": [
"http"
],
"image": "nginx"
},
"Env": null,
"Services": null,
"Vault": null,
"Templates": null,
"Constraints": null,
"Affinities": null,
"Resources": {
"CPU": 100,
"Cores": 0,
"MemoryMB": 300,
"MemoryMaxMB": 0,
"DiskMB": 0,
"IOPS": 0,
"Networks": null,
"Devices": null
},
"RestartPolicy": {
"Attempts": 2,
"Interval": 1800000000000,
"Delay": 15000000000,
"Mode": "fail",
"RenderTemplates": false
},
"DispatchPayload": null,
"Lifecycle": null,
"Meta": null,
"KillTimeout": 5000000000,
"LogConfig": {
"MaxFiles": 10,
"MaxFileSizeMB": 10,
"Disabled": false
},
"Artifacts": null,
"Leader": false,
"ShutdownDelay": 0,
"VolumeMounts": null,
"ScalingPolicies": null,
"KillSignal": "",
"Kind": "",
"CSIPluginConfig": null,
"Identity": null
}
],
"EphemeralDisk": {
"Sticky": false,
"SizeMB": 300,
"Migrate": false
},
"Meta": null,
"ReschedulePolicy": {
"Attempts": 0,
"Interval": 0,
"Delay": 30000000000,
"DelayFunction": "exponential",
"MaxDelay": 3600000000000,
"Unlimited": true
},
"Affinities": null,
"Spreads": null,
"Networks": [
{
"Mode": "",
"Device": "",
"CIDR": "",
"IP": "",
"Hostname": "",
"MBits": 0,
"DNS": null,
"ReservedPorts": null,
"DynamicPorts": [
{
"Label": "http",
"Value": 0,
"To": 80,
"HostNetwork": "default"
}
]
}
],
"Consul": {
"Namespace": ""
},
"Services": [
{
"Name": "job2",
"TaskName": "",
"PortLabel": "http",
"AddressMode": "auto",
"Address": "",
"EnableTagOverride": false,
"Tags": [
"traefik.enable=true"
],
"CanaryTags": null,
"Checks": null,
"Connect": null,
"Meta": null,
"CanaryMeta": null,
"TaggedAddresses": null,
"Namespace": "default",
"OnUpdate": "require_healthy",
"Provider": "nomad"
}
],
"Volumes": null,
"ShutdownDelay": null,
"StopAfterClientDisconnect": null,
"MaxClientDisconnect": null
}
],
"Update": {
"Stagger": 30000000000,
"MaxParallel": 1,
"HealthCheck": "",
"MinHealthyTime": 0,
"HealthyDeadline": 0,
"ProgressDeadline": 0,
"AutoRevert": false,
"AutoPromote": false,
"Canary": 0
},
"Multiregion": null,
"Periodic": null,
"ParameterizedJob": null,
"Dispatched": false,
"DispatchIdempotencyToken": "",
"Payload": null,
"Meta": null,
"ConsulToken": "",
"ConsulNamespace": "",
"VaultToken": "",
"VaultNamespace": "",
"NomadTokenID": "",
"Status": "dead",
"StatusDescription": "",
"Stable": true,
"Version": 10,
"SubmitTime": 1705690880440177400,
"CreateIndex": 2923,
"ModifyIndex": 10048,
"JobModifyIndex": 10044
}

View file

@ -0,0 +1,206 @@
{
"Stop": false,
"Region": "global",
"Namespace": "default",
"ID": "job3",
"ParentID": "",
"Name": "job3",
"Type": "service",
"Priority": 50,
"AllAtOnce": false,
"Datacenters": [
"dc1"
],
"NodePool": "default",
"Constraints": null,
"Affinities": null,
"Spreads": null,
"TaskGroups": [
{
"Name": "group1",
"Count": 1,
"Update": {
"Stagger": 30000000000,
"MaxParallel": 1,
"HealthCheck": "checks",
"MinHealthyTime": 10000000000,
"HealthyDeadline": 300000000000,
"ProgressDeadline": 600000000000,
"AutoRevert": false,
"AutoPromote": false,
"Canary": 0
},
"Migrate": {
"MaxParallel": 1,
"HealthCheck": "checks",
"MinHealthyTime": 10000000000,
"HealthyDeadline": 300000000000
},
"Constraints": [
{
"LTarget": "${attr.nomad.service_discovery}",
"RTarget": "true",
"Operand": "="
}
],
"Scaling": null,
"RestartPolicy": {
"Attempts": 2,
"Interval": 1800000000000,
"Delay": 15000000000,
"Mode": "fail",
"RenderTemplates": false
},
"Tasks": [
{
"Name": "task1",
"Driver": "docker",
"User": "",
"Config": {
"ports": [
"http"
],
"image": "nginx"
},
"Env": null,
"Services": null,
"Vault": null,
"Templates": null,
"Constraints": null,
"Affinities": null,
"Resources": {
"CPU": 100,
"Cores": 0,
"MemoryMB": 300,
"MemoryMaxMB": 0,
"DiskMB": 0,
"IOPS": 0,
"Networks": null,
"Devices": null
},
"RestartPolicy": {
"Attempts": 2,
"Interval": 1800000000000,
"Delay": 15000000000,
"Mode": "fail",
"RenderTemplates": false
},
"DispatchPayload": null,
"Lifecycle": null,
"Meta": null,
"KillTimeout": 5000000000,
"LogConfig": {
"MaxFiles": 10,
"MaxFileSizeMB": 10,
"Disabled": false
},
"Artifacts": null,
"Leader": false,
"ShutdownDelay": 0,
"VolumeMounts": null,
"ScalingPolicies": null,
"KillSignal": "",
"Kind": "",
"CSIPluginConfig": null,
"Identity": null
}
],
"EphemeralDisk": {
"Sticky": false,
"SizeMB": 300,
"Migrate": false
},
"Meta": null,
"ReschedulePolicy": {
"Attempts": 0,
"Interval": 0,
"Delay": 30000000000,
"DelayFunction": "exponential",
"MaxDelay": 3600000000000,
"Unlimited": true
},
"Affinities": null,
"Spreads": null,
"Networks": [
{
"Mode": "",
"Device": "",
"CIDR": "",
"IP": "",
"Hostname": "",
"MBits": 0,
"DNS": null,
"ReservedPorts": null,
"DynamicPorts": [
{
"Label": "http",
"Value": 0,
"To": 80,
"HostNetwork": "default"
}
]
}
],
"Consul": {
"Namespace": ""
},
"Services": [
{
"Name": "job3",
"TaskName": "",
"PortLabel": "http",
"AddressMode": "auto",
"Address": "",
"EnableTagOverride": false,
"Tags": [
"traefik.enable=true"
],
"CanaryTags": null,
"Checks": null,
"Connect": null,
"Meta": null,
"CanaryMeta": null,
"TaggedAddresses": null,
"Namespace": "default",
"OnUpdate": "require_healthy",
"Provider": "nomad"
}
],
"Volumes": null,
"ShutdownDelay": null,
"StopAfterClientDisconnect": null,
"MaxClientDisconnect": null
}
],
"Update": {
"Stagger": 30000000000,
"MaxParallel": 1,
"HealthCheck": "",
"MinHealthyTime": 0,
"HealthyDeadline": 0,
"ProgressDeadline": 0,
"AutoRevert": false,
"AutoPromote": false,
"Canary": 0
},
"Multiregion": null,
"Periodic": null,
"ParameterizedJob": null,
"Dispatched": false,
"DispatchIdempotencyToken": "",
"Payload": null,
"Meta": null,
"ConsulToken": "",
"ConsulNamespace": "",
"VaultToken": "",
"VaultNamespace": "",
"NomadTokenID": "",
"Status": "running",
"StatusDescription": "",
"Stable": true,
"Version": 4,
"SubmitTime": 1705690892484556300,
"CreateIndex": 2932,
"ModifyIndex": 9992,
"JobModifyIndex": 9983
}

View file

@ -0,0 +1,206 @@
{
"Stop": true,
"Region": "global",
"Namespace": "default",
"ID": "job4",
"ParentID": "",
"Name": "job4",
"Type": "service",
"Priority": 50,
"AllAtOnce": false,
"Datacenters": [
"dc1"
],
"NodePool": "default",
"Constraints": null,
"Affinities": null,
"Spreads": null,
"TaskGroups": [
{
"Name": "group1",
"Count": 1,
"Update": {
"Stagger": 30000000000,
"MaxParallel": 1,
"HealthCheck": "checks",
"MinHealthyTime": 10000000000,
"HealthyDeadline": 300000000000,
"ProgressDeadline": 600000000000,
"AutoRevert": false,
"AutoPromote": false,
"Canary": 0
},
"Migrate": {
"MaxParallel": 1,
"HealthCheck": "checks",
"MinHealthyTime": 10000000000,
"HealthyDeadline": 300000000000
},
"Constraints": [
{
"LTarget": "${attr.nomad.service_discovery}",
"RTarget": "true",
"Operand": "="
}
],
"Scaling": null,
"RestartPolicy": {
"Attempts": 2,
"Interval": 1800000000000,
"Delay": 15000000000,
"Mode": "fail",
"RenderTemplates": false
},
"Tasks": [
{
"Name": "task1",
"Driver": "docker",
"User": "",
"Config": {
"image": "nginx",
"ports": [
"http"
]
},
"Env": null,
"Services": null,
"Vault": null,
"Templates": null,
"Constraints": null,
"Affinities": null,
"Resources": {
"CPU": 100,
"Cores": 0,
"MemoryMB": 300,
"MemoryMaxMB": 0,
"DiskMB": 0,
"IOPS": 0,
"Networks": null,
"Devices": null
},
"RestartPolicy": {
"Attempts": 2,
"Interval": 1800000000000,
"Delay": 15000000000,
"Mode": "fail",
"RenderTemplates": false
},
"DispatchPayload": null,
"Lifecycle": null,
"Meta": null,
"KillTimeout": 5000000000,
"LogConfig": {
"MaxFiles": 10,
"MaxFileSizeMB": 10,
"Disabled": false
},
"Artifacts": null,
"Leader": false,
"ShutdownDelay": 0,
"VolumeMounts": null,
"ScalingPolicies": null,
"KillSignal": "",
"Kind": "",
"CSIPluginConfig": null,
"Identity": null
}
],
"EphemeralDisk": {
"Sticky": false,
"SizeMB": 300,
"Migrate": false
},
"Meta": null,
"ReschedulePolicy": {
"Attempts": 0,
"Interval": 0,
"Delay": 30000000000,
"DelayFunction": "exponential",
"MaxDelay": 3600000000000,
"Unlimited": true
},
"Affinities": null,
"Spreads": null,
"Networks": [
{
"Mode": "",
"Device": "",
"CIDR": "",
"IP": "",
"Hostname": "",
"MBits": 0,
"DNS": null,
"ReservedPorts": null,
"DynamicPorts": [
{
"Label": "http",
"Value": 0,
"To": 80,
"HostNetwork": "default"
}
]
}
],
"Consul": {
"Namespace": ""
},
"Services": [
{
"Name": "job4",
"TaskName": "",
"PortLabel": "http",
"AddressMode": "auto",
"Address": "",
"EnableTagOverride": false,
"Tags": [
"traefik.enable=true"
],
"CanaryTags": null,
"Checks": null,
"Connect": null,
"Meta": null,
"CanaryMeta": null,
"TaggedAddresses": null,
"Namespace": "default",
"OnUpdate": "require_healthy",
"Provider": "nomad"
}
],
"Volumes": null,
"ShutdownDelay": null,
"StopAfterClientDisconnect": null,
"MaxClientDisconnect": null
}
],
"Update": {
"Stagger": 30000000000,
"MaxParallel": 1,
"HealthCheck": "",
"MinHealthyTime": 0,
"HealthyDeadline": 0,
"ProgressDeadline": 0,
"AutoRevert": false,
"AutoPromote": false,
"Canary": 0
},
"Multiregion": null,
"Periodic": null,
"ParameterizedJob": null,
"Dispatched": false,
"DispatchIdempotencyToken": "",
"Payload": null,
"Meta": null,
"ConsulToken": "",
"ConsulNamespace": "",
"VaultToken": "",
"VaultNamespace": "",
"NomadTokenID": "",
"Status": "dead",
"StatusDescription": "",
"Stable": true,
"Version": 11,
"SubmitTime": 1705690905317339000,
"CreateIndex": 3079,
"ModifyIndex": 10054,
"JobModifyIndex": 10052
}

View file

@ -0,0 +1,299 @@
{
"Stop": false,
"Region": "global",
"Namespace": "default",
"ID": "job5",
"ParentID": "",
"Name": "job5",
"Type": "service",
"Priority": 50,
"AllAtOnce": false,
"Datacenters": [
"dc1"
],
"NodePool": "default",
"Constraints": null,
"Affinities": null,
"Spreads": null,
"TaskGroups": [
{
"Name": "group1",
"Count": 1,
"Update": {
"Stagger": 30000000000,
"MaxParallel": 1,
"HealthCheck": "checks",
"MinHealthyTime": 10000000000,
"HealthyDeadline": 300000000000,
"ProgressDeadline": 600000000000,
"AutoRevert": false,
"AutoPromote": false,
"Canary": 0
},
"Migrate": {
"MaxParallel": 1,
"HealthCheck": "checks",
"MinHealthyTime": 10000000000,
"HealthyDeadline": 300000000000
},
"Constraints": [
{
"LTarget": "${attr.nomad.service_discovery}",
"RTarget": "true",
"Operand": "="
}
],
"Scaling": {
"ID": "56d57f83-7b9e-fbfa-af96-e2c7f5dc698c",
"Type": "horizontal",
"Target": {
"Namespace": "default",
"Job": "job5",
"Group": "group1"
},
"Policy": null,
"Min": 0,
"Max": 3,
"Enabled": true,
"CreateIndex": 0,
"ModifyIndex": 0
},
"RestartPolicy": {
"Attempts": 2,
"Interval": 1800000000000,
"Delay": 15000000000,
"Mode": "fail",
"RenderTemplates": false
},
"Tasks": [
{
"Name": "task1",
"Driver": "docker",
"User": "",
"Config": {
"image": "nginx",
"ports": [
"http"
]
},
"Env": null,
"Services": [
{
"Name": "job5task1",
"TaskName": "task1",
"PortLabel": "http",
"AddressMode": "auto",
"Address": "",
"EnableTagOverride": false,
"Tags": [
"traefik.enable=true"
],
"CanaryTags": null,
"Checks": null,
"Connect": null,
"Meta": null,
"CanaryMeta": null,
"TaggedAddresses": null,
"Namespace": "default",
"OnUpdate": "require_healthy",
"Provider": "nomad"
}
],
"Vault": null,
"Templates": null,
"Constraints": null,
"Affinities": null,
"Resources": {
"CPU": 100,
"Cores": 0,
"MemoryMB": 300,
"MemoryMaxMB": 0,
"DiskMB": 0,
"IOPS": 0,
"Networks": null,
"Devices": null
},
"RestartPolicy": {
"Attempts": 2,
"Interval": 1800000000000,
"Delay": 15000000000,
"Mode": "fail",
"RenderTemplates": false
},
"DispatchPayload": null,
"Lifecycle": null,
"Meta": null,
"KillTimeout": 5000000000,
"LogConfig": {
"MaxFiles": 10,
"MaxFileSizeMB": 10,
"Disabled": false
},
"Artifacts": null,
"Leader": false,
"ShutdownDelay": 0,
"VolumeMounts": null,
"ScalingPolicies": null,
"KillSignal": "",
"Kind": "",
"CSIPluginConfig": null,
"Identity": null
},
{
"Name": "task2",
"Driver": "docker",
"User": "",
"Config": {
"ports": [
"other"
],
"image": "nginx"
},
"Env": null,
"Services": [
{
"Name": "job5task2",
"TaskName": "task2",
"PortLabel": "other",
"AddressMode": "auto",
"Address": "",
"EnableTagOverride": false,
"Tags": [
"traefik.enable=true"
],
"CanaryTags": null,
"Checks": null,
"Connect": null,
"Meta": null,
"CanaryMeta": null,
"TaggedAddresses": null,
"Namespace": "default",
"OnUpdate": "require_healthy",
"Provider": "nomad"
}
],
"Vault": null,
"Templates": null,
"Constraints": null,
"Affinities": null,
"Resources": {
"CPU": 100,
"Cores": 0,
"MemoryMB": 300,
"MemoryMaxMB": 0,
"DiskMB": 0,
"IOPS": 0,
"Networks": null,
"Devices": null
},
"RestartPolicy": {
"Attempts": 2,
"Interval": 1800000000000,
"Delay": 15000000000,
"Mode": "fail",
"RenderTemplates": false
},
"DispatchPayload": null,
"Lifecycle": null,
"Meta": null,
"KillTimeout": 5000000000,
"LogConfig": {
"MaxFiles": 10,
"MaxFileSizeMB": 10,
"Disabled": false
},
"Artifacts": null,
"Leader": false,
"ShutdownDelay": 0,
"VolumeMounts": null,
"ScalingPolicies": null,
"KillSignal": "",
"Kind": "",
"CSIPluginConfig": null,
"Identity": null
}
],
"EphemeralDisk": {
"Sticky": false,
"SizeMB": 300,
"Migrate": false
},
"Meta": null,
"ReschedulePolicy": {
"Attempts": 0,
"Interval": 0,
"Delay": 30000000000,
"DelayFunction": "exponential",
"MaxDelay": 3600000000000,
"Unlimited": true
},
"Affinities": null,
"Spreads": null,
"Networks": [
{
"Mode": "",
"Device": "",
"CIDR": "",
"IP": "",
"Hostname": "",
"MBits": 0,
"DNS": null,
"ReservedPorts": null,
"DynamicPorts": [
{
"Label": "http",
"Value": 0,
"To": 80,
"HostNetwork": "default"
},
{
"Label": "other",
"Value": 0,
"To": 80,
"HostNetwork": "default"
}
]
}
],
"Consul": {
"Namespace": ""
},
"Services": null,
"Volumes": null,
"ShutdownDelay": null,
"StopAfterClientDisconnect": null,
"MaxClientDisconnect": null
}
],
"Update": {
"Stagger": 30000000000,
"MaxParallel": 1,
"HealthCheck": "",
"MinHealthyTime": 0,
"HealthyDeadline": 0,
"ProgressDeadline": 0,
"AutoRevert": false,
"AutoPromote": false,
"Canary": 0
},
"Multiregion": null,
"Periodic": null,
"ParameterizedJob": null,
"Dispatched": false,
"DispatchIdempotencyToken": "",
"Payload": null,
"Meta": null,
"ConsulToken": "",
"ConsulNamespace": "",
"VaultToken": "",
"VaultNamespace": "",
"NomadTokenID": "",
"Status": "running",
"StatusDescription": "",
"Stable": true,
"Version": 7,
"SubmitTime": 1705690918813110300,
"CreateIndex": 3095,
"ModifyIndex": 10018,
"JobModifyIndex": 10008
}

View file

@ -0,0 +1,299 @@
{
"Stop": false,
"Region": "global",
"Namespace": "default",
"ID": "job6",
"ParentID": "",
"Name": "job6",
"Type": "service",
"Priority": 50,
"AllAtOnce": false,
"Datacenters": [
"dc1"
],
"NodePool": "default",
"Constraints": null,
"Affinities": null,
"Spreads": null,
"TaskGroups": [
{
"Name": "group1",
"Count": 0,
"Update": {
"Stagger": 30000000000,
"MaxParallel": 1,
"HealthCheck": "checks",
"MinHealthyTime": 10000000000,
"HealthyDeadline": 300000000000,
"ProgressDeadline": 600000000000,
"AutoRevert": false,
"AutoPromote": false,
"Canary": 0
},
"Migrate": {
"MaxParallel": 1,
"HealthCheck": "checks",
"MinHealthyTime": 10000000000,
"HealthyDeadline": 300000000000
},
"Constraints": [
{
"LTarget": "${attr.nomad.service_discovery}",
"RTarget": "true",
"Operand": "="
}
],
"Scaling": {
"ID": "d18b02ae-5a68-e635-8100-06b110910b5f",
"Type": "horizontal",
"Target": {
"Job": "job6",
"Group": "group1",
"Namespace": "default"
},
"Policy": null,
"Min": 0,
"Max": 3,
"Enabled": true,
"CreateIndex": 10020,
"ModifyIndex": 10020
},
"RestartPolicy": {
"Attempts": 2,
"Interval": 1800000000000,
"Delay": 15000000000,
"Mode": "fail",
"RenderTemplates": false
},
"Tasks": [
{
"Name": "task1",
"Driver": "docker",
"User": "",
"Config": {
"image": "nginx",
"ports": [
"http"
]
},
"Env": null,
"Services": [
{
"Name": "job6task1",
"TaskName": "task1",
"PortLabel": "http",
"AddressMode": "auto",
"Address": "",
"EnableTagOverride": false,
"Tags": [
"traefik.enable=true"
],
"CanaryTags": null,
"Checks": null,
"Connect": null,
"Meta": null,
"CanaryMeta": null,
"TaggedAddresses": null,
"Namespace": "default",
"OnUpdate": "require_healthy",
"Provider": "nomad"
}
],
"Vault": null,
"Templates": null,
"Constraints": null,
"Affinities": null,
"Resources": {
"CPU": 100,
"Cores": 0,
"MemoryMB": 300,
"MemoryMaxMB": 0,
"DiskMB": 0,
"IOPS": 0,
"Networks": null,
"Devices": null
},
"RestartPolicy": {
"Attempts": 2,
"Interval": 1800000000000,
"Delay": 15000000000,
"Mode": "fail",
"RenderTemplates": false
},
"DispatchPayload": null,
"Lifecycle": null,
"Meta": null,
"KillTimeout": 5000000000,
"LogConfig": {
"MaxFiles": 10,
"MaxFileSizeMB": 10,
"Disabled": false
},
"Artifacts": null,
"Leader": false,
"ShutdownDelay": 0,
"VolumeMounts": null,
"ScalingPolicies": null,
"KillSignal": "",
"Kind": "",
"CSIPluginConfig": null,
"Identity": null
},
{
"Name": "task2",
"Driver": "docker",
"User": "",
"Config": {
"image": "nginx",
"ports": [
"other"
]
},
"Env": null,
"Services": [
{
"Name": "job6task2",
"TaskName": "task2",
"PortLabel": "other",
"AddressMode": "auto",
"Address": "",
"EnableTagOverride": false,
"Tags": [
"traefik.enable=true"
],
"CanaryTags": null,
"Checks": null,
"Connect": null,
"Meta": null,
"CanaryMeta": null,
"TaggedAddresses": null,
"Namespace": "default",
"OnUpdate": "require_healthy",
"Provider": "nomad"
}
],
"Vault": null,
"Templates": null,
"Constraints": null,
"Affinities": null,
"Resources": {
"CPU": 100,
"Cores": 0,
"MemoryMB": 300,
"MemoryMaxMB": 0,
"DiskMB": 0,
"IOPS": 0,
"Networks": null,
"Devices": null
},
"RestartPolicy": {
"Attempts": 2,
"Interval": 1800000000000,
"Delay": 15000000000,
"Mode": "fail",
"RenderTemplates": false
},
"DispatchPayload": null,
"Lifecycle": null,
"Meta": null,
"KillTimeout": 5000000000,
"LogConfig": {
"MaxFiles": 10,
"MaxFileSizeMB": 10,
"Disabled": false
},
"Artifacts": null,
"Leader": false,
"ShutdownDelay": 0,
"VolumeMounts": null,
"ScalingPolicies": null,
"KillSignal": "",
"Kind": "",
"CSIPluginConfig": null,
"Identity": null
}
],
"EphemeralDisk": {
"Sticky": false,
"SizeMB": 300,
"Migrate": false
},
"Meta": null,
"ReschedulePolicy": {
"Attempts": 0,
"Interval": 0,
"Delay": 30000000000,
"DelayFunction": "exponential",
"MaxDelay": 3600000000000,
"Unlimited": true
},
"Affinities": null,
"Spreads": null,
"Networks": [
{
"Mode": "",
"Device": "",
"CIDR": "",
"IP": "",
"Hostname": "",
"MBits": 0,
"DNS": null,
"ReservedPorts": null,
"DynamicPorts": [
{
"Label": "http",
"Value": 0,
"To": 80,
"HostNetwork": "default"
},
{
"Label": "other",
"Value": 0,
"To": 80,
"HostNetwork": "default"
}
]
}
],
"Consul": {
"Namespace": ""
},
"Services": null,
"Volumes": null,
"ShutdownDelay": null,
"StopAfterClientDisconnect": null,
"MaxClientDisconnect": null
}
],
"Update": {
"Stagger": 30000000000,
"MaxParallel": 1,
"HealthCheck": "",
"MinHealthyTime": 0,
"HealthyDeadline": 0,
"ProgressDeadline": 0,
"AutoRevert": false,
"AutoPromote": false,
"Canary": 0
},
"Multiregion": null,
"Periodic": null,
"ParameterizedJob": null,
"Dispatched": false,
"DispatchIdempotencyToken": "",
"Payload": null,
"Meta": null,
"ConsulToken": "",
"ConsulNamespace": "",
"VaultToken": "",
"VaultNamespace": "",
"NomadTokenID": "",
"Status": "dead",
"StatusDescription": "",
"Stable": true,
"Version": 3,
"SubmitTime": 1705690931854150700,
"CreateIndex": 9941,
"ModifyIndex": 10078,
"JobModifyIndex": 10074
}

View file

@ -0,0 +1,222 @@
{
"Stop": false,
"Region": "global",
"Namespace": "default",
"ID": "job7",
"ParentID": "",
"Name": "job7",
"Type": "service",
"Priority": 50,
"AllAtOnce": false,
"Datacenters": [
"dc1"
],
"NodePool": "default",
"Constraints": null,
"Affinities": null,
"Spreads": null,
"TaskGroups": [
{
"Name": "group1",
"Count": 1,
"Update": {
"Stagger": 30000000000,
"MaxParallel": 1,
"HealthCheck": "checks",
"MinHealthyTime": 10000000000,
"HealthyDeadline": 300000000000,
"ProgressDeadline": 600000000000,
"AutoRevert": false,
"AutoPromote": false,
"Canary": 0
},
"Migrate": {
"MaxParallel": 1,
"HealthCheck": "checks",
"MinHealthyTime": 10000000000,
"HealthyDeadline": 300000000000
},
"Constraints": [
{
"LTarget": "${attr.nomad.service_discovery}",
"RTarget": "true",
"Operand": "="
}
],
"Scaling": {
"ID": "e3b82307-1b7f-5fc6-901a-d9174418461f",
"Type": "horizontal",
"Target": {
"Namespace": "default",
"Job": "job7",
"Group": "group1"
},
"Policy": null,
"Min": 0,
"Max": 3,
"Enabled": true,
"CreateIndex": 0,
"ModifyIndex": 0
},
"RestartPolicy": {
"Attempts": 2,
"Interval": 1800000000000,
"Delay": 15000000000,
"Mode": "fail",
"RenderTemplates": false
},
"Tasks": [
{
"Name": "task1",
"Driver": "docker",
"User": "",
"Config": {
"image": "nginx",
"ports": [
"http"
]
},
"Env": null,
"Services": null,
"Vault": null,
"Templates": null,
"Constraints": null,
"Affinities": null,
"Resources": {
"CPU": 100,
"Cores": 0,
"MemoryMB": 300,
"MemoryMaxMB": 0,
"DiskMB": 0,
"IOPS": 0,
"Networks": null,
"Devices": null
},
"RestartPolicy": {
"Attempts": 2,
"Interval": 1800000000000,
"Delay": 15000000000,
"Mode": "fail",
"RenderTemplates": false
},
"DispatchPayload": null,
"Lifecycle": null,
"Meta": null,
"KillTimeout": 5000000000,
"LogConfig": {
"MaxFiles": 10,
"MaxFileSizeMB": 10,
"Disabled": false
},
"Artifacts": null,
"Leader": false,
"ShutdownDelay": 0,
"VolumeMounts": null,
"ScalingPolicies": null,
"KillSignal": "",
"Kind": "",
"CSIPluginConfig": null,
"Identity": null
}
],
"EphemeralDisk": {
"Sticky": false,
"SizeMB": 300,
"Migrate": false
},
"Meta": null,
"ReschedulePolicy": {
"Attempts": 0,
"Interval": 0,
"Delay": 30000000000,
"DelayFunction": "exponential",
"MaxDelay": 3600000000000,
"Unlimited": true
},
"Affinities": null,
"Spreads": null,
"Networks": [
{
"Mode": "",
"Device": "",
"CIDR": "",
"IP": "",
"Hostname": "",
"MBits": 0,
"DNS": null,
"ReservedPorts": null,
"DynamicPorts": [
{
"Label": "http",
"Value": 0,
"To": 80,
"HostNetwork": "default"
}
]
}
],
"Consul": {
"Namespace": ""
},
"Services": [
{
"Name": "job7",
"TaskName": "",
"PortLabel": "http",
"AddressMode": "auto",
"Address": "",
"EnableTagOverride": false,
"Tags": [
"traefik.enable=true",
"traefik.tcp.routers.job7.rule=HostSNI(`job7`)",
"traefik.tcp.routers.job7.tls.passthrough=true"
],
"CanaryTags": null,
"Checks": null,
"Connect": null,
"Meta": null,
"CanaryMeta": null,
"TaggedAddresses": null,
"Namespace": "default",
"OnUpdate": "require_healthy",
"Provider": "nomad"
}
],
"Volumes": null,
"ShutdownDelay": null,
"StopAfterClientDisconnect": null,
"MaxClientDisconnect": null
}
],
"Update": {
"Stagger": 30000000000,
"MaxParallel": 1,
"HealthCheck": "",
"MinHealthyTime": 0,
"HealthyDeadline": 0,
"ProgressDeadline": 0,
"AutoRevert": false,
"AutoPromote": false,
"Canary": 0
},
"Multiregion": null,
"Periodic": null,
"ParameterizedJob": null,
"Dispatched": false,
"DispatchIdempotencyToken": "",
"Payload": null,
"Meta": null,
"ConsulToken": "",
"ConsulNamespace": "",
"VaultToken": "",
"VaultNamespace": "",
"NomadTokenID": "",
"Status": "running",
"StatusDescription": "",
"Stable": true,
"Version": 1,
"SubmitTime": 1705752438438572000,
"CreateIndex": 11221,
"ModifyIndex": 11238,
"JobModifyIndex": 11232
}

View file

@ -0,0 +1,221 @@
{
"Stop": false,
"Region": "global",
"Namespace": "default",
"ID": "job8",
"ParentID": "",
"Name": "job8",
"Type": "service",
"Priority": 50,
"AllAtOnce": false,
"Datacenters": [
"dc1"
],
"NodePool": "default",
"Constraints": null,
"Affinities": null,
"Spreads": null,
"TaskGroups": [
{
"Name": "group1",
"Count": 1,
"Update": {
"Stagger": 30000000000,
"MaxParallel": 1,
"HealthCheck": "checks",
"MinHealthyTime": 10000000000,
"HealthyDeadline": 300000000000,
"ProgressDeadline": 600000000000,
"AutoRevert": false,
"AutoPromote": false,
"Canary": 0
},
"Migrate": {
"MaxParallel": 1,
"HealthCheck": "checks",
"MinHealthyTime": 10000000000,
"HealthyDeadline": 300000000000
},
"Constraints": [
{
"LTarget": "${attr.nomad.service_discovery}",
"RTarget": "true",
"Operand": "="
}
],
"Scaling": {
"ID": "3828aaa9-4e16-cde5-cc80-f0cec4a7c82d",
"Type": "horizontal",
"Target": {
"Namespace": "default",
"Job": "job8",
"Group": "group1"
},
"Policy": null,
"Min": 0,
"Max": 3,
"Enabled": true,
"CreateIndex": 0,
"ModifyIndex": 0
},
"RestartPolicy": {
"Attempts": 2,
"Interval": 1800000000000,
"Delay": 15000000000,
"Mode": "fail",
"RenderTemplates": false
},
"Tasks": [
{
"Name": "task1",
"Driver": "docker",
"User": "",
"Config": {
"ports": [
"http"
],
"image": "nginx"
},
"Env": null,
"Services": null,
"Vault": null,
"Templates": null,
"Constraints": null,
"Affinities": null,
"Resources": {
"CPU": 100,
"Cores": 0,
"MemoryMB": 300,
"MemoryMaxMB": 0,
"DiskMB": 0,
"IOPS": 0,
"Networks": null,
"Devices": null
},
"RestartPolicy": {
"Attempts": 2,
"Interval": 1800000000000,
"Delay": 15000000000,
"Mode": "fail",
"RenderTemplates": false
},
"DispatchPayload": null,
"Lifecycle": null,
"Meta": null,
"KillTimeout": 5000000000,
"LogConfig": {
"MaxFiles": 10,
"MaxFileSizeMB": 10,
"Disabled": false
},
"Artifacts": null,
"Leader": false,
"ShutdownDelay": 0,
"VolumeMounts": null,
"ScalingPolicies": null,
"KillSignal": "",
"Kind": "",
"CSIPluginConfig": null,
"Identity": null
}
],
"EphemeralDisk": {
"Sticky": false,
"SizeMB": 300,
"Migrate": false
},
"Meta": null,
"ReschedulePolicy": {
"Attempts": 0,
"Interval": 0,
"Delay": 30000000000,
"DelayFunction": "exponential",
"MaxDelay": 3600000000000,
"Unlimited": true
},
"Affinities": null,
"Spreads": null,
"Networks": [
{
"Mode": "",
"Device": "",
"CIDR": "",
"IP": "",
"Hostname": "",
"MBits": 0,
"DNS": null,
"ReservedPorts": null,
"DynamicPorts": [
{
"Label": "http",
"Value": 0,
"To": 80,
"HostNetwork": "default"
}
]
}
],
"Consul": {
"Namespace": ""
},
"Services": [
{
"Name": "job8",
"TaskName": "",
"PortLabel": "http",
"AddressMode": "auto",
"Address": "",
"EnableTagOverride": false,
"Tags": [
"traefik.enable=true",
"traefik.udp.routers.job8.service=job8"
],
"CanaryTags": null,
"Checks": null,
"Connect": null,
"Meta": null,
"CanaryMeta": null,
"TaggedAddresses": null,
"Namespace": "default",
"OnUpdate": "require_healthy",
"Provider": "nomad"
}
],
"Volumes": null,
"ShutdownDelay": null,
"StopAfterClientDisconnect": null,
"MaxClientDisconnect": null
}
],
"Update": {
"Stagger": 30000000000,
"MaxParallel": 1,
"HealthCheck": "",
"MinHealthyTime": 0,
"HealthyDeadline": 0,
"ProgressDeadline": 0,
"AutoRevert": false,
"AutoPromote": false,
"Canary": 0
},
"Multiregion": null,
"Periodic": null,
"ParameterizedJob": null,
"Dispatched": false,
"DispatchIdempotencyToken": "",
"Payload": null,
"Meta": null,
"ConsulToken": "",
"ConsulNamespace": "",
"VaultToken": "",
"VaultNamespace": "",
"NomadTokenID": "",
"Status": "running",
"StatusDescription": "",
"Stable": true,
"Version": 2,
"SubmitTime": 1705753285493029600,
"CreateIndex": 11276,
"ModifyIndex": 11303,
"JobModifyIndex": 11297
}

View file

@ -0,0 +1,220 @@
{
"Stop": true,
"Region": "global",
"Namespace": "default",
"ID": "job9",
"ParentID": "",
"Name": "job9",
"Type": "service",
"Priority": 50,
"AllAtOnce": false,
"Datacenters": [
"dc1"
],
"NodePool": "default",
"Constraints": null,
"Affinities": null,
"Spreads": null,
"TaskGroups": [
{
"Name": "group1",
"Count": 1,
"Update": {
"Stagger": 30000000000,
"MaxParallel": 1,
"HealthCheck": "checks",
"MinHealthyTime": 10000000000,
"HealthyDeadline": 300000000000,
"ProgressDeadline": 600000000000,
"AutoRevert": false,
"AutoPromote": false,
"Canary": 0
},
"Migrate": {
"MaxParallel": 1,
"HealthCheck": "checks",
"MinHealthyTime": 10000000000,
"HealthyDeadline": 300000000000
},
"Constraints": [
{
"LTarget": "${attr.nomad.service_discovery}",
"RTarget": "true",
"Operand": "="
}
],
"Scaling": {
"ID": "094d38a7-24cf-d3a4-3a82-cfc50ef7c597",
"Type": "horizontal",
"Target": {
"Group": "group1",
"Namespace": "default",
"Job": "job9"
},
"Policy": null,
"Min": 0,
"Max": 3,
"Enabled": true,
"CreateIndex": 0,
"ModifyIndex": 0
},
"RestartPolicy": {
"Attempts": 2,
"Interval": 1800000000000,
"Delay": 15000000000,
"Mode": "fail",
"RenderTemplates": false
},
"Tasks": [
{
"Name": "task1",
"Driver": "docker",
"User": "",
"Config": {
"image": "nginx",
"ports": [
"http"
]
},
"Env": null,
"Services": null,
"Vault": null,
"Templates": null,
"Constraints": null,
"Affinities": null,
"Resources": {
"CPU": 100,
"Cores": 0,
"MemoryMB": 300,
"MemoryMaxMB": 0,
"DiskMB": 0,
"IOPS": 0,
"Networks": null,
"Devices": null
},
"RestartPolicy": {
"Attempts": 2,
"Interval": 1800000000000,
"Delay": 15000000000,
"Mode": "fail",
"RenderTemplates": false
},
"DispatchPayload": null,
"Lifecycle": null,
"Meta": null,
"KillTimeout": 5000000000,
"LogConfig": {
"MaxFiles": 10,
"MaxFileSizeMB": 10,
"Disabled": false
},
"Artifacts": null,
"Leader": false,
"ShutdownDelay": 0,
"VolumeMounts": null,
"ScalingPolicies": null,
"KillSignal": "",
"Kind": "",
"CSIPluginConfig": null,
"Identity": null
}
],
"EphemeralDisk": {
"Sticky": false,
"SizeMB": 300,
"Migrate": false
},
"Meta": null,
"ReschedulePolicy": {
"Attempts": 0,
"Interval": 0,
"Delay": 30000000000,
"DelayFunction": "exponential",
"MaxDelay": 3600000000000,
"Unlimited": true
},
"Affinities": null,
"Spreads": null,
"Networks": [
{
"Mode": "",
"Device": "",
"CIDR": "",
"IP": "",
"Hostname": "",
"MBits": 0,
"DNS": null,
"ReservedPorts": null,
"DynamicPorts": [
{
"Label": "http",
"Value": 0,
"To": 80,
"HostNetwork": "default"
}
]
}
],
"Consul": {
"Namespace": ""
},
"Services": [
{
"Name": "job9",
"TaskName": "",
"PortLabel": "http",
"AddressMode": "auto",
"Address": "",
"EnableTagOverride": false,
"Tags": [
"traefik.enable=true"
],
"CanaryTags": null,
"Checks": null,
"Connect": null,
"Meta": null,
"CanaryMeta": null,
"TaggedAddresses": null,
"Namespace": "default",
"OnUpdate": "require_healthy",
"Provider": "nomad"
}
],
"Volumes": null,
"ShutdownDelay": null,
"StopAfterClientDisconnect": null,
"MaxClientDisconnect": null
}
],
"Update": {
"Stagger": 30000000000,
"MaxParallel": 1,
"HealthCheck": "",
"MinHealthyTime": 0,
"HealthyDeadline": 0,
"ProgressDeadline": 0,
"AutoRevert": false,
"AutoPromote": false,
"Canary": 0
},
"Multiregion": null,
"Periodic": null,
"ParameterizedJob": null,
"Dispatched": false,
"DispatchIdempotencyToken": "",
"Payload": null,
"Meta": null,
"ConsulToken": "",
"ConsulNamespace": "",
"VaultToken": "",
"VaultNamespace": "",
"NomadTokenID": "",
"Status": "dead",
"StatusDescription": "",
"Stable": true,
"Version": 3,
"SubmitTime": 1705753979676559600,
"CreateIndex": 11317,
"ModifyIndex": 11346,
"JobModifyIndex": 11344
}

View file

@ -0,0 +1,56 @@
[
{
"ID": "job1",
"ParentID": "",
"Name": "job1",
"Namespace": "default",
"Datacenters": [
"dc1"
],
"NodePool": "default",
"Multiregion": null,
"Type": "service",
"Priority": 50,
"Periodic": false,
"ParameterizedJob": false,
"Stop": false,
"Status": "running",
"StatusDescription": "",
"JobSummary": {
"JobID": "job1",
"Namespace": "default",
"Summary": {
"job1": {
"Queued": 0,
"Complete": 1,
"Failed": 0,
"Running": 0,
"Starting": 0,
"Lost": 0,
"Unknown": 0
},
"group1": {
"Queued": 0,
"Complete": 6,
"Failed": 1,
"Running": 1,
"Starting": 0,
"Lost": 0,
"Unknown": 0
}
},
"Children": {
"Pending": 0,
"Running": 0,
"Dead": 0
},
"CreateIndex": 493,
"ModifyIndex": 9909
},
"CreateIndex": 493,
"ModifyIndex": 9961,
"JobModifyIndex": 9955,
"SubmitTime": 1705690395733241600,
"Meta": null
}
]

View file

@ -0,0 +1,47 @@
[
{
"ID": "job2",
"ParentID": "",
"Name": "job2",
"Namespace": "default",
"Datacenters": [
"dc1"
],
"NodePool": "default",
"Multiregion": null,
"Type": "service",
"Priority": 50,
"Periodic": false,
"ParameterizedJob": false,
"Stop": false,
"Status": "dead",
"StatusDescription": "",
"JobSummary": {
"JobID": "job2",
"Namespace": "default",
"Summary": {
"group1": {
"Queued": 0,
"Complete": 6,
"Failed": 6,
"Running": 0,
"Starting": 0,
"Lost": 0,
"Unknown": 0
}
},
"Children": {
"Pending": 0,
"Running": 0,
"Dead": 0
},
"CreateIndex": 2923,
"ModifyIndex": 10051
},
"CreateIndex": 2923,
"ModifyIndex": 10048,
"JobModifyIndex": 10044,
"SubmitTime": 1705690880440177400,
"Meta": null
}
]

View file

@ -0,0 +1,47 @@
[
{
"ID": "job3",
"ParentID": "",
"Name": "job3",
"Namespace": "default",
"Datacenters": [
"dc1"
],
"NodePool": "default",
"Multiregion": null,
"Type": "service",
"Priority": 50,
"Periodic": false,
"ParameterizedJob": false,
"Stop": false,
"Status": "running",
"StatusDescription": "",
"JobSummary": {
"JobID": "job3",
"Namespace": "default",
"Summary": {
"group1": {
"Queued": 0,
"Complete": 1,
"Failed": 0,
"Running": 1,
"Starting": 0,
"Lost": 0,
"Unknown": 0
}
},
"Children": {
"Pending": 0,
"Running": 0,
"Dead": 0
},
"CreateIndex": 2932,
"ModifyIndex": 9989
},
"CreateIndex": 2932,
"ModifyIndex": 9992,
"JobModifyIndex": 9983,
"SubmitTime": 1705690892484556300,
"Meta": null
}
]

View file

@ -0,0 +1,47 @@
[
{
"ID": "job4",
"ParentID": "",
"Name": "job4",
"Namespace": "default",
"Datacenters": [
"dc1"
],
"NodePool": "default",
"Multiregion": null,
"Type": "service",
"Priority": 50,
"Periodic": false,
"ParameterizedJob": false,
"Stop": true,
"Status": "dead",
"StatusDescription": "",
"JobSummary": {
"JobID": "job4",
"Namespace": "default",
"Summary": {
"group1": {
"Queued": 0,
"Complete": 6,
"Failed": 0,
"Running": 0,
"Starting": 0,
"Lost": 0,
"Unknown": 0
}
},
"Children": {
"Pending": 0,
"Running": 0,
"Dead": 0
},
"CreateIndex": 3079,
"ModifyIndex": 10057
},
"CreateIndex": 3079,
"ModifyIndex": 10054,
"JobModifyIndex": 10052,
"SubmitTime": 1705690905317339000,
"Meta": null
}
]

View file

@ -0,0 +1,56 @@
[
{
"ID": "job5",
"ParentID": "",
"Name": "job5",
"Namespace": "default",
"Datacenters": [
"dc1"
],
"NodePool": "default",
"Multiregion": null,
"Type": "service",
"Priority": 50,
"Periodic": false,
"ParameterizedJob": false,
"Stop": false,
"Status": "running",
"StatusDescription": "",
"JobSummary": {
"JobID": "job5",
"Namespace": "default",
"Summary": {
"job5": {
"Queued": 0,
"Complete": 2,
"Failed": 0,
"Running": 0,
"Starting": 0,
"Lost": 0,
"Unknown": 0
},
"group1": {
"Queued": 0,
"Complete": 4,
"Failed": 0,
"Running": 1,
"Starting": 0,
"Lost": 0,
"Unknown": 0
}
},
"Children": {
"Pending": 0,
"Running": 0,
"Dead": 0
},
"CreateIndex": 3095,
"ModifyIndex": 10015
},
"CreateIndex": 3095,
"ModifyIndex": 10018,
"JobModifyIndex": 10008,
"SubmitTime": 1705690918813110300,
"Meta": null
}
]

View file

@ -0,0 +1,47 @@
[
{
"ID": "job6",
"ParentID": "",
"Name": "job6",
"Namespace": "default",
"Datacenters": [
"dc1"
],
"NodePool": "default",
"Multiregion": null,
"Type": "service",
"Priority": 50,
"Periodic": false,
"ParameterizedJob": false,
"Stop": false,
"Status": "dead",
"StatusDescription": "",
"JobSummary": {
"JobID": "job6",
"Namespace": "default",
"Summary": {
"group1": {
"Queued": 0,
"Complete": 3,
"Failed": 0,
"Running": 0,
"Starting": 0,
"Lost": 0,
"Unknown": 0
}
},
"Children": {
"Pending": 0,
"Running": 0,
"Dead": 0
},
"CreateIndex": 9941,
"ModifyIndex": 10082
},
"CreateIndex": 9941,
"ModifyIndex": 10078,
"JobModifyIndex": 10074,
"SubmitTime": 1705690931854150700,
"Meta": null
}
]

View file

@ -0,0 +1,47 @@
[
{
"ID": "job7",
"ParentID": "",
"Name": "job7",
"Namespace": "default",
"Datacenters": [
"dc1"
],
"NodePool": "default",
"Multiregion": null,
"Type": "service",
"Priority": 50,
"Periodic": false,
"ParameterizedJob": false,
"Stop": false,
"Status": "running",
"StatusDescription": "",
"JobSummary": {
"JobID": "job7",
"Namespace": "default",
"Summary": {
"group1": {
"Queued": 0,
"Complete": 0,
"Failed": 0,
"Running": 1,
"Starting": 0,
"Lost": 0,
"Unknown": 0
}
},
"Children": {
"Pending": 0,
"Running": 0,
"Dead": 0
},
"CreateIndex": 11221,
"ModifyIndex": 11225
},
"CreateIndex": 11221,
"ModifyIndex": 11238,
"JobModifyIndex": 11232,
"SubmitTime": 1705752438438572000,
"Meta": null
}
]

View file

@ -0,0 +1,47 @@
[
{
"ID": "job8",
"ParentID": "",
"Name": "job8",
"Namespace": "default",
"Datacenters": [
"dc1"
],
"NodePool": "default",
"Multiregion": null,
"Type": "service",
"Priority": 50,
"Periodic": false,
"ParameterizedJob": false,
"Stop": false,
"Status": "running",
"StatusDescription": "",
"JobSummary": {
"JobID": "job8",
"Namespace": "default",
"Summary": {
"group1": {
"Queued": 0,
"Complete": 0,
"Failed": 0,
"Running": 1,
"Starting": 0,
"Lost": 0,
"Unknown": 0
}
},
"Children": {
"Pending": 0,
"Running": 0,
"Dead": 0
},
"CreateIndex": 11276,
"ModifyIndex": 11281
},
"CreateIndex": 11276,
"ModifyIndex": 11303,
"JobModifyIndex": 11297,
"SubmitTime": 1705753285493029600,
"Meta": null
}
]

View file

@ -0,0 +1,47 @@
[
{
"ID": "job9",
"ParentID": "",
"Name": "job9",
"Namespace": "default",
"Datacenters": [
"dc1"
],
"NodePool": "default",
"Multiregion": null,
"Type": "service",
"Priority": 50,
"Periodic": false,
"ParameterizedJob": false,
"Stop": true,
"Status": "dead",
"StatusDescription": "",
"JobSummary": {
"JobID": "job9",
"Namespace": "default",
"Summary": {
"group1": {
"Queued": 0,
"Complete": 1,
"Failed": 0,
"Running": 0,
"Starting": 0,
"Lost": 0,
"Unknown": 0
}
},
"Children": {
"Pending": 0,
"Running": 0,
"Dead": 0
},
"CreateIndex": 11317,
"ModifyIndex": 11349
},
"CreateIndex": 11317,
"ModifyIndex": 11346,
"JobModifyIndex": 11344,
"SubmitTime": 1705753979676559600,
"Meta": null
}
]

View file

@ -0,0 +1,20 @@
[
{
"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"
]
}
]

View file

@ -0,0 +1,18 @@
[
{
"ID": "_nomad-task-03c7270c-f475-5981-1932-87c0a8a5aa24-group-group1-job1-http",
"ServiceName": "job1",
"Namespace": "default",
"NodeID": "e262ecb6-a7ac-7c3e-4de0-06445559e596",
"Datacenter": "dc1",
"JobID": "job1",
"AllocID": "03c7270c-f475-5981-1932-87c0a8a5aa24",
"Tags": [
"traefik.enable=true"
],
"Address": "192.168.1.21",
"Port": 29916,
"CreateIndex": 5753,
"ModifyIndex": 9958
}
]

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1,18 @@
[
{
"ID": "_nomad-task-dd945b55-70fa-0efc-6512-b88fb55ce33f-group-group1-job3-http",
"ServiceName": "job3",
"Namespace": "default",
"NodeID": "e262ecb6-a7ac-7c3e-4de0-06445559e596",
"Datacenter": "dc1",
"JobID": "job3",
"AllocID": "dd945b55-70fa-0efc-6512-b88fb55ce33f",
"Tags": [
"traefik.enable=true"
],
"Address": "192.168.1.21",
"Port": 29983,
"CreateIndex": 9987,
"ModifyIndex": 9987
}
]

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1,18 @@
[
{
"ID": "_nomad-task-a98bac3d-5382-3032-1954-57aff58b20c1-task1-job5task1-http",
"ServiceName": "job5task1",
"Namespace": "default",
"NodeID": "e262ecb6-a7ac-7c3e-4de0-06445559e596",
"Datacenter": "dc1",
"JobID": "job5",
"AllocID": "a98bac3d-5382-3032-1954-57aff58b20c1",
"Tags": [
"traefik.enable=true"
],
"Address": "192.168.1.21",
"Port": 24542,
"CreateIndex": 10013,
"ModifyIndex": 10013
}
]

View file

@ -0,0 +1,18 @@
[
{
"ID": "_nomad-task-a98bac3d-5382-3032-1954-57aff58b20c1-task2-job5task2-other",
"ServiceName": "job5task2",
"Namespace": "default",
"NodeID": "e262ecb6-a7ac-7c3e-4de0-06445559e596",
"Datacenter": "dc1",
"JobID": "job5",
"AllocID": "a98bac3d-5382-3032-1954-57aff58b20c1",
"Tags": [
"traefik.enable=true"
],
"Address": "192.168.1.21",
"Port": 30165,
"CreateIndex": 10014,
"ModifyIndex": 10014
}
]

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1,20 @@
[
{
"ID": "_nomad-task-3e1cc853-f6b1-2c46-6f20-332a6e91794b-group-group1-job7-http",
"ServiceName": "job7",
"Namespace": "default",
"NodeID": "e262ecb6-a7ac-7c3e-4de0-06445559e596",
"Datacenter": "dc1",
"JobID": "job7",
"AllocID": "3e1cc853-f6b1-2c46-6f20-332a6e91794b",
"Tags": [
"traefik.enable=true",
"traefik.tcp.routers.job7.rule=HostSNI(`job7`)",
"traefik.tcp.routers.job7.tls.passthrough=true"
],
"Address": "192.168.1.21",
"Port": 22899,
"CreateIndex": 11224,
"ModifyIndex": 11271
}
]

View file

@ -0,0 +1,19 @@
[
{
"ID": "_nomad-task-fffd3d81-66ac-ed6d-d9a1-fc41b2a26d07-group-group1-job8-http",
"ServiceName": "job8",
"Namespace": "default",
"NodeID": "e262ecb6-a7ac-7c3e-4de0-06445559e596",
"Datacenter": "dc1",
"JobID": "job8",
"AllocID": "fffd3d81-66ac-ed6d-d9a1-fc41b2a26d07",
"Tags": [
"traefik.enable=true",
"traefik.udp.routers.job8.service=job8"
],
"Address": "192.168.1.21",
"Port": 24268,
"CreateIndex": 11279,
"ModifyIndex": 11300
}
]

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1,18 @@
[
{
"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"
]
}
]

View file

@ -0,0 +1,21 @@
[
{
"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"
]
}
]
}
]

View file

@ -85,13 +85,14 @@ func (p *ProviderBuilder) BuildProviders() []*Provider {
// Configuration represents the Nomad provider configuration. // Configuration represents the Nomad provider configuration.
type Configuration struct { type Configuration struct {
DefaultRule string `description:"Default rule." json:"defaultRule,omitempty" toml:"defaultRule,omitempty" yaml:"defaultRule,omitempty"` 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"` 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"` 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"` 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"` Stale bool `description:"Use stale consistency for catalog reads." json:"stale,omitempty" toml:"stale,omitempty" yaml:"stale,omitempty" export:"true"`
ExposedByDefault bool `description:"Expose Nomad services by default." json:"exposedByDefault,omitempty" toml:"exposedByDefault,omitempty" yaml:"exposedByDefault,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"` RefreshInterval ptypes.Duration `description:"Interval for polling Nomad API." json:"refreshInterval,omitempty" toml:"refreshInterval,omitempty" yaml:"refreshInterval,omitempty" export:"true"`
AllowEmptyServices bool `description:"Allow the creation of services without endpoints." json:"allowEmptyServices,omitempty" toml:"allowEmptyServices,omitempty" yaml:"allowEmptyServices,omitempty" export:"true"`
} }
// SetDefaults sets the default values for the Nomad Traefik Provider Configuration. // SetDefaults sets the default values for the Nomad Traefik Provider Configuration.
@ -116,6 +117,7 @@ func (c *Configuration) SetDefaults() {
c.ExposedByDefault = true c.ExposedByDefault = true
c.RefreshInterval = ptypes.Duration(15 * time.Second) c.RefreshInterval = ptypes.Duration(15 * time.Second)
c.DefaultRule = defaultTemplateRule c.DefaultRule = defaultTemplateRule
c.AllowEmptyServices = false
} }
type EndpointConfig struct { type EndpointConfig struct {
@ -222,10 +224,20 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
} }
func (p *Provider) loadConfiguration(ctx context.Context, configurationC chan<- dynamic.Message) error { func (p *Provider) loadConfiguration(ctx context.Context, configurationC chan<- dynamic.Message) error {
items, err := p.getNomadServiceData(ctx) var items []item
if err != nil { var err error
return err if p.AllowEmptyServices {
items, err = p.getNomadServiceDataWithEmptyServices(ctx)
if err != nil {
return err
}
} else {
items, err = p.getNomadServiceData(ctx)
if err != nil {
return err
}
} }
configurationC <- dynamic.Message{ configurationC <- dynamic.Message{
ProviderName: p.name, ProviderName: p.name,
Configuration: p.buildConfig(ctx, items), Configuration: p.buildConfig(ctx, items),
@ -291,6 +303,98 @@ func (p *Provider) getNomadServiceData(ctx context.Context) ([]item, error) {
return items, nil return items, nil
} }
func (p *Provider) getNomadServiceDataWithEmptyServices(ctx context.Context) ([]item, error) {
jobsOpts := &api.QueryOptions{AllowStale: p.Stale}
jobsOpts = jobsOpts.WithContext(ctx)
jobStubs, _, err := p.client.Jobs().List(jobsOpts)
if err != nil {
return nil, err
}
var items []item
// Get Services even when they are scaled down to zero. Currently the nomad service interface does not support this. https://github.com/hashicorp/nomad/issues/19731
for _, jobStub := range jobStubs {
jobInfoOpts := &api.QueryOptions{}
jobInfoOpts = jobInfoOpts.WithContext(ctx)
job, _, err := p.client.Jobs().Info(jobStub.ID, jobInfoOpts)
if err != nil {
return nil, err
}
for _, taskGroup := range job.TaskGroups {
services := []*api.Service{}
// Get all services in job -> taskgroup
services = append(services, taskGroup.Services...)
// Get all services in job -> taskgroup -> tasks
for _, task := range taskGroup.Tasks {
services = append(services, task.Services...)
}
for _, service := range services {
logger := log.Ctx(ctx).With().Str("serviceName", service.TaskName).Logger()
extraConf := p.getExtraConf(service.Tags)
if !extraConf.Enable {
logger.Debug().Msg("Filter Nomad service that is not enabled")
continue
}
matches, err := constraints.MatchTags(service.Tags, p.Constraints)
if err != nil {
logger.Error().Err(err).Msg("Error matching constraint expressions")
continue
}
if !matches {
logger.Debug().Msgf("Filter Nomad service not matching constraints: %q", p.Constraints)
continue
}
if nil != taskGroup.Scaling && *taskGroup.Scaling.Enabled && *taskGroup.Count == 0 {
// Add items without address
items = append(items, item{
// Create a unique id for non registered services
ID: fmt.Sprintf("%s-%s-%s-%s-%s", *job.Namespace, *job.Name, *taskGroup.Name, service.TaskName, service.Name),
Name: service.Name,
Namespace: *job.Namespace,
Node: "",
Datacenter: "",
Address: "",
Port: -1,
Tags: service.Tags,
ExtraConf: p.getExtraConf(service.Tags),
})
} else {
instances, err := p.fetchService(ctx, service.Name)
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.getExtraConf(i.Tags),
})
}
}
}
}
}
return items, nil
}
// getExtraConf returns a configuration with settings which are not part of the dynamic configuration (e.g. "<prefix>.enable"). // getExtraConf returns a configuration with settings which are not part of the dynamic configuration (e.g. "<prefix>.enable").
func (p *Provider) getExtraConf(tags []string) configuration { func (p *Provider) getExtraConf(tags []string) configuration {
labels := tagsToLabels(tags, p.Prefix) labels := tagsToLabels(tags, p.Prefix)

View file

@ -2,8 +2,11 @@ package nomad
import ( import (
"context" "context"
"fmt"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os"
"path/filepath"
"strings" "strings"
"testing" "testing"
@ -12,6 +15,17 @@ import (
"github.com/traefik/traefik/v3/pkg/types" "github.com/traefik/traefik/v3/pkg/types"
) )
var responses = map[string][]byte{}
func TestMain(m *testing.M) {
err := setup()
if err != nil {
fmt.Fprintf(os.Stderr, "%s", err.Error())
os.Exit(1)
}
m.Run()
}
func Test_globalConfig(t *testing.T) { func Test_globalConfig(t *testing.T) {
cases := []struct { cases := []struct {
Name string Name string
@ -131,15 +145,311 @@ func TestProvider_SetDefaults_Endpoint(t *testing.T) {
} }
} }
func Test_getNomadServiceDataWithEmptyServices_GroupService_Scaling1(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case strings.HasSuffix(r.RequestURI, "/v1/jobs"):
_, _ = w.Write(responses["jobs_job1"])
case strings.HasSuffix(r.RequestURI, "/v1/job/job1"):
_, _ = w.Write(responses["job_job1_WithGroupService_Scaling1"])
case strings.HasSuffix(r.RequestURI, "/v1/service/job1"):
_, _ = w.Write(responses["service_job1"])
}
}))
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.getNomadServiceDataWithEmptyServices(context.TODO())
require.NoError(t, err)
require.Len(t, items, 1)
}
func Test_getNomadServiceDataWithEmptyServices_GroupService_Scaling0(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case strings.HasSuffix(r.RequestURI, "/v1/jobs"):
_, _ = w.Write(responses["jobs_job2"])
case strings.HasSuffix(r.RequestURI, "/v1/job/job2"):
_, _ = w.Write(responses["job_job2_WithGroupService_Scaling0"])
case strings.HasSuffix(r.RequestURI, "/v1/service/job2"):
_, _ = w.Write(responses["service_job2"])
}
}))
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.getNomadServiceDataWithEmptyServices(context.TODO())
require.NoError(t, err)
require.Len(t, items, 1)
}
func Test_getNomadServiceDataWithEmptyServices_GroupService_ScalingDisabled(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case strings.HasSuffix(r.RequestURI, "/v1/jobs"):
_, _ = w.Write(responses["jobs_job3"])
case strings.HasSuffix(r.RequestURI, "/v1/job/job3"):
_, _ = w.Write(responses["job_job3_WithGroupService_ScalingDisabled"])
case strings.HasSuffix(r.RequestURI, "/v1/service/job3"):
_, _ = w.Write(responses["service_job3"])
}
}))
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.getNomadServiceDataWithEmptyServices(context.TODO())
require.NoError(t, err)
require.Len(t, items, 1)
}
func Test_getNomadServiceDataWithEmptyServices_GroupService_ScalingDisabled_Stopped(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case strings.HasSuffix(r.RequestURI, "/v1/jobs"):
_, _ = w.Write(responses["jobs_job4"])
case strings.HasSuffix(r.RequestURI, "/v1/job/job4"):
_, _ = w.Write(responses["job_job4_WithGroupService_ScalingDisabled_Stopped"])
case strings.HasSuffix(r.RequestURI, "/v1/service/job4"):
_, _ = w.Write(responses["service_job4"])
}
}))
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.getNomadServiceDataWithEmptyServices(context.TODO())
require.NoError(t, err)
// Should not be listed as job is stopped
require.Empty(t, items)
}
func Test_getNomadServiceDataWithEmptyServices_GroupTaskService_Scaling1(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case strings.HasSuffix(r.RequestURI, "/v1/jobs"):
_, _ = w.Write(responses["jobs_job5"])
case strings.HasSuffix(r.RequestURI, "/v1/job/job5"):
_, _ = w.Write(responses["job_job5_WithGroupTaskService_Scaling1"])
case strings.HasSuffix(r.RequestURI, "/v1/service/job5task1"):
_, _ = w.Write(responses["service_job5task1"])
case strings.HasSuffix(r.RequestURI, "/v1/service/job5task2"):
_, _ = w.Write(responses["service_job5task2"])
}
}))
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.getNomadServiceDataWithEmptyServices(context.TODO())
require.NoError(t, err)
require.Len(t, items, 2)
}
func Test_getNomadServiceDataWithEmptyServices_GroupTaskService_Scaling0(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case strings.HasSuffix(r.RequestURI, "/v1/jobs"):
_, _ = w.Write(responses["jobs_job6"])
case strings.HasSuffix(r.RequestURI, "/v1/job/job6"):
_, _ = w.Write(responses["job_job6_WithGroupTaskService_Scaling0"])
case strings.HasSuffix(r.RequestURI, "/v1/service/job6task1"):
_, _ = w.Write(responses["service_job6task1"])
case strings.HasSuffix(r.RequestURI, "/v1/service/job6task2"):
_, _ = w.Write(responses["service_job6task2"])
}
}))
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.getNomadServiceDataWithEmptyServices(context.TODO())
require.NoError(t, err)
require.Len(t, items, 2)
}
func Test_getNomadServiceDataWithEmptyServices_TCP(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case strings.HasSuffix(r.RequestURI, "/v1/jobs"):
_, _ = w.Write(responses["jobs_job7"])
case strings.HasSuffix(r.RequestURI, "/v1/job/job7"):
_, _ = w.Write(responses["job_job7_TCP"])
case strings.HasSuffix(r.RequestURI, "/v1/service/job7"):
_, _ = w.Write(responses["service_job7"])
}
}))
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.getNomadServiceDataWithEmptyServices(context.TODO())
require.NoError(t, err)
require.Len(t, items, 1)
}
func Test_getNomadServiceDataWithEmptyServices_UDP(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case strings.HasSuffix(r.RequestURI, "/v1/jobs"):
_, _ = w.Write(responses["jobs_job8"])
case strings.HasSuffix(r.RequestURI, "/v1/job/job8"):
_, _ = w.Write(responses["job_job8_UDP"])
case strings.HasSuffix(r.RequestURI, "/v1/service/job8"):
_, _ = w.Write(responses["service_job8"])
}
}))
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.getNomadServiceDataWithEmptyServices(context.TODO())
require.NoError(t, err)
require.Len(t, items, 1)
}
func Test_getNomadServiceDataWithEmptyServices_ScalingEnabled_Stopped(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case strings.HasSuffix(r.RequestURI, "/v1/jobs"):
_, _ = w.Write(responses["jobs_job9"])
case strings.HasSuffix(r.RequestURI, "/v1/job/job9"):
_, _ = w.Write(responses["job_job9_ScalingEnabled_Stopped"])
case strings.HasSuffix(r.RequestURI, "/v1/service/job9"):
_, _ = w.Write(responses["service_job9"])
}
}))
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.getNomadServiceDataWithEmptyServices(context.TODO())
require.NoError(t, err)
// Should not be listed as job is stopped
require.Empty(t, items)
}
func setup() error {
responsesDir := "./fixtures"
files, err := os.ReadDir(responsesDir)
if err != nil {
return err
}
for _, file := range files {
if !file.IsDir() && filepath.Ext(file.Name()) == ".json" {
content, err := os.ReadFile(filepath.Join(responsesDir, file.Name()))
if err != nil {
return err
}
responses[strings.TrimSuffix(filepath.Base(file.Name()), filepath.Ext(file.Name()))] = content
}
}
return nil
}
func Test_getNomadServiceData(t *testing.T) { func Test_getNomadServiceData(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch { switch {
case strings.HasSuffix(r.RequestURI, "/v1/services"): case strings.HasSuffix(r.RequestURI, "/v1/services"):
_, _ = w.Write([]byte(services)) _, _ = w.Write(responses["services"])
case strings.HasSuffix(r.RequestURI, "/v1/service/redis"): case strings.HasSuffix(r.RequestURI, "/v1/service/redis"):
_, _ = w.Write([]byte(redis)) _, _ = w.Write(responses["service_redis"])
case strings.HasSuffix(r.RequestURI, "/v1/service/hello-nomad"): case strings.HasSuffix(r.RequestURI, "/v1/service/hello-nomad"):
_, _ = w.Write([]byte(hello)) _, _ = w.Write(responses["service_hello"])
} }
})) }))
t.Cleanup(ts.Close) t.Cleanup(ts.Close)
@ -159,71 +469,3 @@ func Test_getNomadServiceData(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Len(t, items, 2) 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"
]
}
]
`