From 1391c359789d8dbcaefedf54c0f87ba9178c75bf Mon Sep 17 00:00:00 2001 From: Emile Vauge Date: Thu, 14 Mar 2024 16:18:04 +0100 Subject: [PATCH 01/16] Add youkoulayley to maintainers --- docs/content/contributing/maintainers.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/content/contributing/maintainers.md b/docs/content/contributing/maintainers.md index d379677e8..45f03c65f 100644 --- a/docs/content/contributing/maintainers.md +++ b/docs/content/contributing/maintainers.md @@ -21,6 +21,7 @@ description: "Traefik Proxy is an open source software with a thriving community * Harold Ozouf [@jspdown](https://github.com/jspdown) * Tom Moulard [@tommoulard](https://github.com/tommoulard) * Landry Benguigui [@lbenguigui](https://github.com/lbenguigui) +* Baptiste Mayelle [@youkoulayley](https://github.com/youkoulayley) ## Past Maintainers From 75790e0ab8bd0dcf510e11d4e9c81c4c6f0c766c Mon Sep 17 00:00:00 2001 From: Emile Vauge Date: Thu, 14 Mar 2024 16:54:04 +0100 Subject: [PATCH 02/16] Add sdelicata to maintainers --- docs/content/contributing/maintainers.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/content/contributing/maintainers.md b/docs/content/contributing/maintainers.md index 45f03c65f..e91bfcc89 100644 --- a/docs/content/contributing/maintainers.md +++ b/docs/content/contributing/maintainers.md @@ -21,6 +21,7 @@ description: "Traefik Proxy is an open source software with a thriving community * Harold Ozouf [@jspdown](https://github.com/jspdown) * Tom Moulard [@tommoulard](https://github.com/tommoulard) * Landry Benguigui [@lbenguigui](https://github.com/lbenguigui) +* Simon Delicata [@sdelicata](https://github.com/sdelicata) * Baptiste Mayelle [@youkoulayley](https://github.com/youkoulayley) ## Past Maintainers From fc875b38e0accf9bdb2c20796521a6339cff6d5f Mon Sep 17 00:00:00 2001 From: shivanipawar00 <50794721+shivanipawar00@users.noreply.github.com> Date: Tue, 19 Mar 2024 20:30:05 +0530 Subject: [PATCH 03/16] Added specification for TCP TLS routers in documentation --- docs/content/routing/routers/index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/content/routing/routers/index.md b/docs/content/routing/routers/index.md index 537e81ee1..6fedaa945 100644 --- a/docs/content/routing/routers/index.md +++ b/docs/content/routing/routers/index.md @@ -667,7 +667,8 @@ The [supported `provider` table](../../https/acme.md#providers) indicates if the ### General -If both HTTP routers and TCP routers listen to the same entry points, the TCP routers will apply *before* the HTTP routers. +For non-TLS connections, if HTTP and TCP routers listen on the same EntryPoint, the TCP routers will apply *before* the HTTP routers. +For TLS connections, if HTTPS and TCP-TLS routers listen on the same EntryPoint, the HTTPS routers will apply *before* the TCP-TLS routers. If no matching route is found for the TCP routers, then the HTTP routers will take over. ### EntryPoints From 141abce2d5f37856465087d83fa099e464a24dda Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Wed, 20 Mar 2024 10:26:03 +0100 Subject: [PATCH 04/16] chore: update linter --- .github/workflows/validate.yaml | 2 +- .golangci.yml | 7 ++++--- .semaphore/semaphore.yml | 2 +- cmd/traefik/traefik_test.go | 1 - integration/https_test.go | 4 ---- pkg/api/dashboard/dashboard_test.go | 2 -- pkg/api/handler_entrypoint_test.go | 1 - pkg/api/handler_http_test.go | 1 - pkg/api/handler_overview_test.go | 1 - pkg/api/handler_tcp_test.go | 1 - pkg/api/handler_test.go | 2 -- pkg/api/handler_udp_test.go | 1 - pkg/collector/collector.go | 2 +- pkg/config/kv/kv_node_test.go | 1 - pkg/config/kv/kv_test.go | 1 - pkg/config/runtime/runtime_http_test.go | 1 - pkg/config/runtime/runtime_tcp_test.go | 1 - pkg/config/runtime/runtime_test.go | 2 -- pkg/config/runtime/runtime_udp_test.go | 1 - pkg/config/static/static_config_test.go | 1 - pkg/healthcheck/healthcheck.go | 3 +-- pkg/healthcheck/healthcheck_test.go | 3 --- pkg/ip/checker_test.go | 3 --- pkg/ip/strategy_test.go | 3 --- pkg/log/log_test.go | 1 - pkg/metrics/prometheus_test.go | 2 -- .../accesslog/logger_formatters_test.go | 2 -- pkg/middlewares/accesslog/logger_test.go | 3 --- pkg/middlewares/accesslog/parser_test.go | 1 - pkg/middlewares/accesslog/save_retries_test.go | 2 -- pkg/middlewares/addprefix/add_prefix_test.go | 2 -- pkg/middlewares/auth/basic_auth_test.go | 1 - pkg/middlewares/auth/digest_auth_test.go | 1 - pkg/middlewares/buffering/buffering_test.go | 1 - pkg/middlewares/capture/capture_test.go | 1 - pkg/middlewares/compress/compress_test.go | 3 --- .../connectionheader/connectionheader_test.go | 1 - .../customerrors/custom_errors_test.go | 2 -- .../empty_backend_handler_test.go | 1 - .../forwardedheaders/forwarded_header_test.go | 2 -- pkg/middlewares/headers/header_test.go | 1 - pkg/middlewares/headers/headers_test.go | 1 - pkg/middlewares/headers/secure_test.go | 1 - pkg/middlewares/ipallowlist/ip_allowlist_test.go | 2 -- pkg/middlewares/ipwhitelist/ip_whitelist_test.go | 2 -- pkg/middlewares/metrics/metrics_test.go | 2 -- .../pass_tls_client_cert_test.go | 4 ---- pkg/middlewares/pipelining/pipelining_test.go | 1 - pkg/middlewares/ratelimiter/rate_limiter_test.go | 2 -- pkg/middlewares/redirect/redirect_regex_test.go | 1 - pkg/middlewares/redirect/redirect_scheme_test.go | 2 -- .../requestdecorator/hostresolver_test.go | 1 - .../requestdecorator/request_decorator_test.go | 3 --- pkg/middlewares/retry/retry_test.go | 2 -- pkg/middlewares/snicheck/snicheck_test.go | 1 - pkg/middlewares/stripprefix/strip_prefix_test.go | 1 - .../stripprefixregex/strip_prefix_regex_test.go | 1 - .../tcp/ipallowlist/ip_allowlist_test.go | 2 -- .../tcp/ipwhitelist/ip_whitelist_test.go | 2 -- pkg/muxer/http/mux_test.go | 7 ------- pkg/muxer/tcp/mux_test.go | 16 ---------------- pkg/provider/acme/local_store_test.go | 1 - pkg/provider/acme/provider_test.go | 6 ------ pkg/provider/aggregator/aggregator.go | 1 - .../constraints/constraints_labels_test.go | 1 - .../constraints/constraints_tags_test.go | 1 - pkg/provider/consulcatalog/config_test.go | 5 ----- pkg/provider/consulcatalog/convert_types_test.go | 1 - pkg/provider/docker/config_test.go | 8 -------- pkg/provider/docker/docker.go | 3 +-- pkg/provider/docker/swarm_test.go | 3 --- pkg/provider/ecs/config_test.go | 3 --- pkg/provider/ecs/ecs_test.go | 1 - pkg/provider/kubernetes/crd/kubernetes_test.go | 14 -------------- pkg/provider/kubernetes/gateway/client_test.go | 2 -- .../kubernetes/gateway/kubernetes_test.go | 11 ----------- .../kubernetes/ingress/annotations_test.go | 3 --- pkg/provider/kubernetes/ingress/client_test.go | 2 -- .../kubernetes/ingress/kubernetes_test.go | 7 ------- .../kubernetes/k8s/event_handler_test.go | 1 - pkg/provider/kv/consul/consul_test.go | 2 -- pkg/provider/marathon/builder_test.go | 3 +-- pkg/provider/marathon/config_test.go | 3 --- pkg/provider/marathon/label_test.go | 1 - pkg/provider/marathon/readiness_test.go | 1 - pkg/provider/nomad/config_test.go | 2 -- pkg/provider/nomad/nomad_test.go | 1 - pkg/provider/nomad/tag_test.go | 1 - pkg/provider/rancher/config_test.go | 2 -- pkg/provider/traefik/internal_test.go | 1 - pkg/redactor/redactor_doOnJSON_test.go | 1 - pkg/redactor/redactor_doOnStruct_test.go | 1 - pkg/rules/parser.go | 1 - pkg/rules/parser_test.go | 2 -- pkg/safe/routine_test.go | 1 - pkg/server/aggregator_test.go | 6 ------ pkg/server/cookie/cookie_test.go | 2 -- pkg/server/middleware/middlewares_test.go | 2 -- pkg/server/provider/provider_test.go | 2 -- pkg/server/router/router.go | 1 - pkg/server/router/router_test.go | 2 -- pkg/server/router/tcp/manager.go | 2 -- pkg/server/router/tcp/manager_test.go | 2 -- pkg/server/router/tcp/router_test.go | 2 -- pkg/server/router/udp/router.go | 2 -- pkg/server/router/udp/router_test.go | 2 -- pkg/server/routerfactory_test.go | 2 -- pkg/server/service/roundtripper_test.go | 2 -- pkg/server/service/service_test.go | 3 --- pkg/server/service/tcp/service_test.go | 1 - pkg/server/service/udp/service_test.go | 1 - pkg/tcp/proxy_test.go | 3 --- pkg/tcp/wrr_load_balancer_test.go | 2 -- pkg/tls/certificate_store_test.go | 1 - pkg/tls/tlsmanager_test.go | 2 -- pkg/tracing/operation_name_test.go | 3 --- pkg/types/domain_test.go | 3 --- pkg/types/tls_test.go | 2 -- 118 files changed, 10 insertions(+), 265 deletions(-) diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml index 076a36b64..1ba3a6d61 100644 --- a/.github/workflows/validate.yaml +++ b/.github/workflows/validate.yaml @@ -7,7 +7,7 @@ on: env: GO_VERSION: '1.22' - GOLANGCI_LINT_VERSION: v1.56.2 + GOLANGCI_LINT_VERSION: v1.57.0 MISSSPELL_VERSION: v0.4.1 jobs: diff --git a/.golangci.yml b/.golangci.yml index 1a7860b91..743021e87 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,8 +1,5 @@ run: timeout: 10m - skip-files: [] - skip-dirs: - - pkg/provider/kubernetes/crd/generated/ linters-settings: govet: @@ -208,11 +205,15 @@ linters: - maintidx # kind of duplicate of gocyclo - nonamedreturns # Too strict - gosmopolitan # not relevant + - exportloopref # Useless with go1.22 + - musttag issues: exclude-use-default: false max-issues-per-linter: 0 max-same-issues: 0 + exclude-dirs: + - pkg/provider/kubernetes/crd/generated/ exclude: - 'Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked' - "should have a package comment, unless it's in another file for this package" diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index e45bca92c..ede5885d5 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -25,7 +25,7 @@ global_job_config: - export "PATH=${GOPATH}/bin:${PATH}" - mkdir -vp "${SEMAPHORE_GIT_DIR}" "${GOPATH}/bin" - export GOPROXY=https://proxy.golang.org,direct - - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "${GOPATH}/bin" v1.56.2 + - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "${GOPATH}/bin" v1.57.0 - curl -sSfL https://gist.githubusercontent.com/traefiker/6d7ac019c11d011e4f131bb2cca8900e/raw/goreleaser.sh | bash -s -- -b "${GOPATH}/bin" - checkout - cache restore traefik-$(checksum go.sum) diff --git a/cmd/traefik/traefik_test.go b/cmd/traefik/traefik_test.go index 26bc8643c..4b6cdcef3 100644 --- a/cmd/traefik/traefik_test.go +++ b/cmd/traefik/traefik_test.go @@ -94,7 +94,6 @@ func TestAppendCertMetric(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/integration/https_test.go b/integration/https_test.go index a4f7dc527..87a05480c 100644 --- a/integration/https_test.go +++ b/integration/https_test.go @@ -1133,8 +1133,6 @@ func (s *HTTPSSuite) TestWithDomainFronting() { } for _, test := range testCases { - test := test - req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:4443", nil) require.NoError(s.T(), err) req.Host = test.hostHeader @@ -1178,8 +1176,6 @@ func (s *HTTPSSuite) TestWithInvalidTLSOption() { } for _, test := range testCases { - test := test - tlsConfig := &tls.Config{ InsecureSkipVerify: true, } diff --git a/pkg/api/dashboard/dashboard_test.go b/pkg/api/dashboard/dashboard_test.go index ef0ffc635..28c734d94 100644 --- a/pkg/api/dashboard/dashboard_test.go +++ b/pkg/api/dashboard/dashboard_test.go @@ -42,7 +42,6 @@ func Test_safePrefix(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -91,7 +90,6 @@ func Test_ContentSecurityPolicy(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/api/handler_entrypoint_test.go b/pkg/api/handler_entrypoint_test.go index 36010cebe..d5d6a5d1f 100644 --- a/pkg/api/handler_entrypoint_test.go +++ b/pkg/api/handler_entrypoint_test.go @@ -210,7 +210,6 @@ func TestHandler_EntryPoints(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/api/handler_http_test.go b/pkg/api/handler_http_test.go index b814c69ab..7ed879a97 100644 --- a/pkg/api/handler_http_test.go +++ b/pkg/api/handler_http_test.go @@ -920,7 +920,6 @@ func TestHandler_HTTP(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/api/handler_overview_test.go b/pkg/api/handler_overview_test.go index 428687e69..422c35f21 100644 --- a/pkg/api/handler_overview_test.go +++ b/pkg/api/handler_overview_test.go @@ -275,7 +275,6 @@ func TestHandler_Overview(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/api/handler_tcp_test.go b/pkg/api/handler_tcp_test.go index 85e5fdc19..af79bbce8 100644 --- a/pkg/api/handler_tcp_test.go +++ b/pkg/api/handler_tcp_test.go @@ -791,7 +791,6 @@ func TestHandler_TCP(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/api/handler_test.go b/pkg/api/handler_test.go index 7bca3921a..9a2e3ae76 100644 --- a/pkg/api/handler_test.go +++ b/pkg/api/handler_test.go @@ -127,7 +127,6 @@ func TestHandler_RawData(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -252,7 +251,6 @@ func TestHandler_GetMiddleware(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/api/handler_udp_test.go b/pkg/api/handler_udp_test.go index 56c9b3a6f..9f230c466 100644 --- a/pkg/api/handler_udp_test.go +++ b/pkg/api/handler_udp_test.go @@ -530,7 +530,6 @@ func TestHandler_UDP(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/collector/collector.go b/pkg/collector/collector.go index 2ecec4d1d..50ce0666d 100644 --- a/pkg/collector/collector.go +++ b/pkg/collector/collector.go @@ -65,7 +65,7 @@ func createBody(staticConfiguration *static.Configuration) (*bytes.Buffer, error } buf := new(bytes.Buffer) - err = json.NewEncoder(buf).Encode(data) //nolint:musttag // cannot be changed for historical reasons. + err = json.NewEncoder(buf).Encode(data) if err != nil { return nil, err } diff --git a/pkg/config/kv/kv_node_test.go b/pkg/config/kv/kv_node_test.go index c4a76b843..86b7d6ea2 100644 --- a/pkg/config/kv/kv_node_test.go +++ b/pkg/config/kv/kv_node_test.go @@ -244,7 +244,6 @@ func TestDecodeToNode(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/config/kv/kv_test.go b/pkg/config/kv/kv_test.go index 9dafcaab4..2a951cc51 100644 --- a/pkg/config/kv/kv_test.go +++ b/pkg/config/kv/kv_test.go @@ -87,7 +87,6 @@ func TestDecode(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/config/runtime/runtime_http_test.go b/pkg/config/runtime/runtime_http_test.go index 9ac028bfb..7ca957472 100644 --- a/pkg/config/runtime/runtime_http_test.go +++ b/pkg/config/runtime/runtime_http_test.go @@ -208,7 +208,6 @@ func TestGetRoutersByEntryPoints(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() runtimeConfig := NewConfig(test.conf) diff --git a/pkg/config/runtime/runtime_tcp_test.go b/pkg/config/runtime/runtime_tcp_test.go index 067e9ee09..b30509325 100644 --- a/pkg/config/runtime/runtime_tcp_test.go +++ b/pkg/config/runtime/runtime_tcp_test.go @@ -208,7 +208,6 @@ func TestGetTCPRoutersByEntryPoints(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() runtimeConfig := NewConfig(test.conf) diff --git a/pkg/config/runtime/runtime_test.go b/pkg/config/runtime/runtime_test.go index 3a7588be3..7b309b53d 100644 --- a/pkg/config/runtime/runtime_test.go +++ b/pkg/config/runtime/runtime_test.go @@ -663,8 +663,6 @@ func TestPopulateUsedBy(t *testing.T) { }, } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/config/runtime/runtime_udp_test.go b/pkg/config/runtime/runtime_udp_test.go index 87fe2476f..c43972674 100644 --- a/pkg/config/runtime/runtime_udp_test.go +++ b/pkg/config/runtime/runtime_udp_test.go @@ -189,7 +189,6 @@ func TestGetUDPRoutersByEntryPoints(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() runtimeConfig := NewConfig(test.conf) diff --git a/pkg/config/static/static_config_test.go b/pkg/config/static/static_config_test.go index 0fbd83056..691540580 100644 --- a/pkg/config/static/static_config_test.go +++ b/pkg/config/static/static_config_test.go @@ -26,7 +26,6 @@ func TestHasEntrypoint(t *testing.T) { } for _, test := range tests { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/healthcheck/healthcheck.go b/pkg/healthcheck/healthcheck.go index 68210e821..8bd8d1e17 100644 --- a/pkg/healthcheck/healthcheck.go +++ b/pkg/healthcheck/healthcheck.go @@ -137,9 +137,8 @@ func (hc *HealthCheck) SetBackendsConfiguration(parentCtx context.Context, backe hc.cancel = cancel for _, backend := range backends { - currentBackend := backend safe.Go(func() { - hc.execute(ctx, currentBackend) + hc.execute(ctx, backend) }) } } diff --git a/pkg/healthcheck/healthcheck_test.go b/pkg/healthcheck/healthcheck_test.go index 5b1ba2f39..73254b27e 100644 --- a/pkg/healthcheck/healthcheck_test.go +++ b/pkg/healthcheck/healthcheck_test.go @@ -96,7 +96,6 @@ func TestSetBackendsConfiguration(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -268,7 +267,6 @@ func TestNewRequest(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -358,7 +356,6 @@ func TestRequestOptions(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/ip/checker_test.go b/pkg/ip/checker_test.go index da2f07861..53bcb1311 100644 --- a/pkg/ip/checker_test.go +++ b/pkg/ip/checker_test.go @@ -36,7 +36,6 @@ func TestIsAuthorized(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -117,7 +116,6 @@ func TestNew(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -289,7 +287,6 @@ func TestContainsIsAllowed(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/ip/strategy_test.go b/pkg/ip/strategy_test.go index 1409b1d54..8bc5d285e 100644 --- a/pkg/ip/strategy_test.go +++ b/pkg/ip/strategy_test.go @@ -21,7 +21,6 @@ func TestRemoteAddrStrategy_GetIP(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -61,7 +60,6 @@ func TestDepthStrategy_GetIP(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -109,7 +107,6 @@ func TestTrustedIPsStrategy_GetIP(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/log/log_test.go b/pkg/log/log_test.go index c81724ff3..704f2a83b 100644 --- a/pkg/log/log_test.go +++ b/pkg/log/log_test.go @@ -38,7 +38,6 @@ func TestLog(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { var buffer bytes.Buffer SetOutput(&buffer) diff --git a/pkg/metrics/prometheus_test.go b/pkg/metrics/prometheus_test.go index 33b6ba80d..7be5cbf64 100644 --- a/pkg/metrics/prometheus_test.go +++ b/pkg/metrics/prometheus_test.go @@ -64,7 +64,6 @@ func TestRegisterPromState(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { actualNbRegistries := 0 for _, prom := range test.prometheusSlice { @@ -419,7 +418,6 @@ func TestPrometheus(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.name, func(t *testing.T) { family := findMetricFamily(test.name, metricsFamilies) if family == nil { diff --git a/pkg/middlewares/accesslog/logger_formatters_test.go b/pkg/middlewares/accesslog/logger_formatters_test.go index cfa4281ec..028e3dbf7 100644 --- a/pkg/middlewares/accesslog/logger_formatters_test.go +++ b/pkg/middlewares/accesslog/logger_formatters_test.go @@ -86,7 +86,6 @@ func TestCommonLogFormatter_Format(t *testing.T) { t.Setenv("TZ", "Etc/GMT+9") for _, test := range testCases { - test := test t.Run(test.name, func(t *testing.T) { t.Parallel() @@ -150,7 +149,6 @@ func Test_toLog(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/accesslog/logger_test.go b/pkg/middlewares/accesslog/logger_test.go index b599e3e8d..ebec9173e 100644 --- a/pkg/middlewares/accesslog/logger_test.go +++ b/pkg/middlewares/accesslog/logger_test.go @@ -178,7 +178,6 @@ func TestLoggerHeaderFields(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { logFile, err := os.CreateTemp(t.TempDir(), "*.log") require.NoError(t, err) @@ -466,7 +465,6 @@ func TestLoggerJSON(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -684,7 +682,6 @@ func TestNewLogHandlerOutputStdout(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { // NOTE: It is not possible to run these cases in parallel because we capture Stdout diff --git a/pkg/middlewares/accesslog/parser_test.go b/pkg/middlewares/accesslog/parser_test.go index 1712f1a8c..1e194dd7e 100644 --- a/pkg/middlewares/accesslog/parser_test.go +++ b/pkg/middlewares/accesslog/parser_test.go @@ -60,7 +60,6 @@ func TestParseAccessLog(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/accesslog/save_retries_test.go b/pkg/middlewares/accesslog/save_retries_test.go index add4cc28f..ca296b2a5 100644 --- a/pkg/middlewares/accesslog/save_retries_test.go +++ b/pkg/middlewares/accesslog/save_retries_test.go @@ -28,8 +28,6 @@ func TestSaveRetries(t *testing.T) { } for _, test := range tests { - test := test - t.Run(fmt.Sprintf("%d retries", test.requestAttempt), func(t *testing.T) { t.Parallel() saveRetries := &SaveRetries{} diff --git a/pkg/middlewares/addprefix/add_prefix_test.go b/pkg/middlewares/addprefix/add_prefix_test.go index 9ec4df5fa..e56a13d9f 100644 --- a/pkg/middlewares/addprefix/add_prefix_test.go +++ b/pkg/middlewares/addprefix/add_prefix_test.go @@ -29,7 +29,6 @@ func TestNewAddPrefix(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -75,7 +74,6 @@ func TestAddPrefix(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/auth/basic_auth_test.go b/pkg/middlewares/auth/basic_auth_test.go index 893443b96..196bd3669 100644 --- a/pkg/middlewares/auth/basic_auth_test.go +++ b/pkg/middlewares/auth/basic_auth_test.go @@ -210,7 +210,6 @@ func TestBasicAuthUsersFromFile(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/auth/digest_auth_test.go b/pkg/middlewares/auth/digest_auth_test.go index a972223b4..1494205cd 100644 --- a/pkg/middlewares/auth/digest_auth_test.go +++ b/pkg/middlewares/auth/digest_auth_test.go @@ -88,7 +88,6 @@ func TestDigestAuthUsersFromFile(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/buffering/buffering_test.go b/pkg/middlewares/buffering/buffering_test.go index 2d8eec37e..adf2c6daa 100644 --- a/pkg/middlewares/buffering/buffering_test.go +++ b/pkg/middlewares/buffering/buffering_test.go @@ -48,7 +48,6 @@ func TestBuffering(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/capture/capture_test.go b/pkg/middlewares/capture/capture_test.go index 2254b0fea..7ef7fcade 100644 --- a/pkg/middlewares/capture/capture_test.go +++ b/pkg/middlewares/capture/capture_test.go @@ -217,7 +217,6 @@ func TestCloseNotifier(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/compress/compress_test.go b/pkg/middlewares/compress/compress_test.go index 3bdaf9678..353174fa7 100644 --- a/pkg/middlewares/compress/compress_test.go +++ b/pkg/middlewares/compress/compress_test.go @@ -124,7 +124,6 @@ func TestShouldNotCompressWhenSpecificContentType(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -326,8 +325,6 @@ func TestMinResponseBodyBytes(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.name, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/connectionheader/connectionheader_test.go b/pkg/middlewares/connectionheader/connectionheader_test.go index 7ee047bd0..bd41d58d8 100644 --- a/pkg/middlewares/connectionheader/connectionheader_test.go +++ b/pkg/middlewares/connectionheader/connectionheader_test.go @@ -47,7 +47,6 @@ func TestRemover(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/customerrors/custom_errors_test.go b/pkg/middlewares/customerrors/custom_errors_test.go index 437c560cb..046ecb665 100644 --- a/pkg/middlewares/customerrors/custom_errors_test.go +++ b/pkg/middlewares/customerrors/custom_errors_test.go @@ -157,7 +157,6 @@ func TestHandler(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -293,7 +292,6 @@ func TestNewResponseRecorder(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/emptybackendhandler/empty_backend_handler_test.go b/pkg/middlewares/emptybackendhandler/empty_backend_handler_test.go index fddb9c768..299fe9c31 100644 --- a/pkg/middlewares/emptybackendhandler/empty_backend_handler_test.go +++ b/pkg/middlewares/emptybackendhandler/empty_backend_handler_test.go @@ -28,7 +28,6 @@ func TestEmptyBackendHandler(t *testing.T) { } for _, test := range testCases { - test := test t.Run(fmt.Sprintf("amount servers %d", test.amountServer), func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/forwardedheaders/forwarded_header_test.go b/pkg/middlewares/forwardedheaders/forwarded_header_test.go index 4d2dab2fc..8e1d10925 100644 --- a/pkg/middlewares/forwardedheaders/forwarded_header_test.go +++ b/pkg/middlewares/forwardedheaders/forwarded_header_test.go @@ -272,7 +272,6 @@ func TestServeHTTP(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -369,7 +368,6 @@ func Test_isWebsocketRequest(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/headers/header_test.go b/pkg/middlewares/headers/header_test.go index 363b7e737..17745e826 100644 --- a/pkg/middlewares/headers/header_test.go +++ b/pkg/middlewares/headers/header_test.go @@ -52,7 +52,6 @@ func TestNewHeader_customRequestHeader(t *testing.T) { emptyHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/headers/headers_test.go b/pkg/middlewares/headers/headers_test.go index ceef014dd..41ec273a2 100644 --- a/pkg/middlewares/headers/headers_test.go +++ b/pkg/middlewares/headers/headers_test.go @@ -59,7 +59,6 @@ func TestNew_allowedHosts(t *testing.T) { require.NoError(t, err) for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/headers/secure_test.go b/pkg/middlewares/headers/secure_test.go index ce2b93a7c..050288dfa 100644 --- a/pkg/middlewares/headers/secure_test.go +++ b/pkg/middlewares/headers/secure_test.go @@ -180,7 +180,6 @@ func Test_newSecure_modifyResponse(t *testing.T) { emptyHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }) for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/ipallowlist/ip_allowlist_test.go b/pkg/middlewares/ipallowlist/ip_allowlist_test.go index df2e49835..130c6f7cc 100644 --- a/pkg/middlewares/ipallowlist/ip_allowlist_test.go +++ b/pkg/middlewares/ipallowlist/ip_allowlist_test.go @@ -33,7 +33,6 @@ func TestNewIPAllowLister(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -76,7 +75,6 @@ func TestIPAllowLister_ServeHTTP(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/ipwhitelist/ip_whitelist_test.go b/pkg/middlewares/ipwhitelist/ip_whitelist_test.go index 2fabbfae7..13b6e9e5e 100644 --- a/pkg/middlewares/ipwhitelist/ip_whitelist_test.go +++ b/pkg/middlewares/ipwhitelist/ip_whitelist_test.go @@ -33,7 +33,6 @@ func TestNewIPWhiteLister(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -76,7 +75,6 @@ func TestIPWhiteLister_ServeHTTP(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/metrics/metrics_test.go b/pkg/middlewares/metrics/metrics_test.go index 9390e49f5..c6b9f7eae 100644 --- a/pkg/middlewares/metrics/metrics_test.go +++ b/pkg/middlewares/metrics/metrics_test.go @@ -86,7 +86,6 @@ func TestCloseNotifier(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -120,7 +119,6 @@ func Test_getMethod(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.method, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/passtlsclientcert/pass_tls_client_cert_test.go b/pkg/middlewares/passtlsclientcert/pass_tls_client_cert_test.go index 96c1bdffa..911fd3cb7 100644 --- a/pkg/middlewares/passtlsclientcert/pass_tls_client_cert_test.go +++ b/pkg/middlewares/passtlsclientcert/pass_tls_client_cert_test.go @@ -310,7 +310,6 @@ func TestPassTLSClientCert_PEM(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -533,7 +532,6 @@ func TestPassTLSClientCert_certInfo(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -604,7 +602,6 @@ WqeUSNGYV//RunTeuRDAf5OxehERb1srzBXhRZ3cZdzXbgR/`, } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -645,7 +642,6 @@ func Test_getSANs(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/pipelining/pipelining_test.go b/pkg/middlewares/pipelining/pipelining_test.go index 80a03d2d6..9ad64f103 100644 --- a/pkg/middlewares/pipelining/pipelining_test.go +++ b/pkg/middlewares/pipelining/pipelining_test.go @@ -54,7 +54,6 @@ func TestNew(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/ratelimiter/rate_limiter_test.go b/pkg/middlewares/ratelimiter/rate_limiter_test.go index 51a077819..e3ff92ec5 100644 --- a/pkg/middlewares/ratelimiter/rate_limiter_test.go +++ b/pkg/middlewares/ratelimiter/rate_limiter_test.go @@ -89,7 +89,6 @@ func TestNewRateLimiter(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -248,7 +247,6 @@ func TestRateLimit(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { if test.loadDuration >= time.Minute && testing.Short() { t.Skip("skipping test in short mode.") diff --git a/pkg/middlewares/redirect/redirect_regex_test.go b/pkg/middlewares/redirect/redirect_regex_test.go index d6346cf75..0c04d9ef8 100644 --- a/pkg/middlewares/redirect/redirect_regex_test.go +++ b/pkg/middlewares/redirect/redirect_regex_test.go @@ -154,7 +154,6 @@ func TestRedirectRegexHandler(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/redirect/redirect_scheme_test.go b/pkg/middlewares/redirect/redirect_scheme_test.go index 8de531a2c..7ff97bba4 100644 --- a/pkg/middlewares/redirect/redirect_scheme_test.go +++ b/pkg/middlewares/redirect/redirect_scheme_test.go @@ -283,8 +283,6 @@ func TestRedirectSchemeHandler(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/requestdecorator/hostresolver_test.go b/pkg/middlewares/requestdecorator/hostresolver_test.go index ed199c387..f1c8c39a2 100644 --- a/pkg/middlewares/requestdecorator/hostresolver_test.go +++ b/pkg/middlewares/requestdecorator/hostresolver_test.go @@ -35,7 +35,6 @@ func TestCNAMEFlatten(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/requestdecorator/request_decorator_test.go b/pkg/middlewares/requestdecorator/request_decorator_test.go index 03c8f4018..c289fcaec 100644 --- a/pkg/middlewares/requestdecorator/request_decorator_test.go +++ b/pkg/middlewares/requestdecorator/request_decorator_test.go @@ -38,7 +38,6 @@ func TestRequestHost(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -80,7 +79,6 @@ func TestRequestFlattening(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -173,7 +171,6 @@ func Test_parseHost(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/retry/retry_test.go b/pkg/middlewares/retry/retry_test.go index 5645e007c..be2606ce4 100644 --- a/pkg/middlewares/retry/retry_test.go +++ b/pkg/middlewares/retry/retry_test.go @@ -101,7 +101,6 @@ func TestRetry(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -272,7 +271,6 @@ func TestRetryWebsocket(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/snicheck/snicheck_test.go b/pkg/middlewares/snicheck/snicheck_test.go index a9d57bdf6..d7411e555 100644 --- a/pkg/middlewares/snicheck/snicheck_test.go +++ b/pkg/middlewares/snicheck/snicheck_test.go @@ -37,7 +37,6 @@ func TestSNICheck_ServeHTTP(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/stripprefix/strip_prefix_test.go b/pkg/middlewares/stripprefix/strip_prefix_test.go index 5c9d7171e..b592224e9 100644 --- a/pkg/middlewares/stripprefix/strip_prefix_test.go +++ b/pkg/middlewares/stripprefix/strip_prefix_test.go @@ -178,7 +178,6 @@ func TestStripPrefix(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/stripprefixregex/strip_prefix_regex_test.go b/pkg/middlewares/stripprefixregex/strip_prefix_regex_test.go index 763985ba9..18e831d35 100644 --- a/pkg/middlewares/stripprefixregex/strip_prefix_regex_test.go +++ b/pkg/middlewares/stripprefixregex/strip_prefix_regex_test.go @@ -108,7 +108,6 @@ func TestStripPrefixRegex(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.path, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/tcp/ipallowlist/ip_allowlist_test.go b/pkg/middlewares/tcp/ipallowlist/ip_allowlist_test.go index 4f3b59e15..d55a1fffa 100644 --- a/pkg/middlewares/tcp/ipallowlist/ip_allowlist_test.go +++ b/pkg/middlewares/tcp/ipallowlist/ip_allowlist_test.go @@ -39,7 +39,6 @@ func TestNewIPAllowLister(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -81,7 +80,6 @@ func TestIPAllowLister_ServeHTTP(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/middlewares/tcp/ipwhitelist/ip_whitelist_test.go b/pkg/middlewares/tcp/ipwhitelist/ip_whitelist_test.go index 67780dfb6..e419f6bfe 100644 --- a/pkg/middlewares/tcp/ipwhitelist/ip_whitelist_test.go +++ b/pkg/middlewares/tcp/ipwhitelist/ip_whitelist_test.go @@ -39,7 +39,6 @@ func TestNewIPWhiteLister(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -81,7 +80,6 @@ func TestIPWhiteLister_ServeHTTP(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/muxer/http/mux_test.go b/pkg/muxer/http/mux_test.go index 0f6649dc7..768c7b898 100644 --- a/pkg/muxer/http/mux_test.go +++ b/pkg/muxer/http/mux_test.go @@ -653,8 +653,6 @@ func Test_addRoute(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -808,14 +806,12 @@ func Test_addRoutePriority(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() muxer, err := NewMuxer() require.NoError(t, err) for _, route := range test.cases { - route := route handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-From", route.xFrom) }) @@ -900,7 +896,6 @@ func TestHostRegexp(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -964,7 +959,6 @@ func TestParseDomains(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.expression, func(t *testing.T) { t.Parallel() @@ -1027,7 +1021,6 @@ func TestAbsoluteFormURL(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/muxer/tcp/mux_test.go b/pkg/muxer/tcp/mux_test.go index 827d295b1..45820acdb 100644 --- a/pkg/muxer/tcp/mux_test.go +++ b/pkg/muxer/tcp/mux_test.go @@ -502,8 +502,6 @@ func Test_addTCPRoute(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -614,7 +612,6 @@ func TestParseHostSNI(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.expression, func(t *testing.T) { t.Parallel() @@ -659,8 +656,6 @@ func Test_HostSNICatchAll(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -761,8 +756,6 @@ func Test_HostSNI(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -876,8 +869,6 @@ func Test_HostSNIRegexp(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -969,8 +960,6 @@ func Test_ClientIP(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -1038,8 +1027,6 @@ func Test_ALPN(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -1107,8 +1094,6 @@ func Test_Priority(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -1117,7 +1102,6 @@ func Test_Priority(t *testing.T) { matchedRule := "" for rule, priority := range test.rules { - rule := rule err := muxer.AddRoute(rule, priority, tcp.HandlerFunc(func(conn tcp.WriteCloser) { matchedRule = rule })) diff --git a/pkg/provider/acme/local_store_test.go b/pkg/provider/acme/local_store_test.go index 44d4ab0fc..4614078c0 100644 --- a/pkg/provider/acme/local_store_test.go +++ b/pkg/provider/acme/local_store_test.go @@ -44,7 +44,6 @@ func TestLocalStore_GetAccount(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { s := NewLocalStore(test.filename) diff --git a/pkg/provider/acme/provider_test.go b/pkg/provider/acme/provider_test.go index 2a55de2c9..8a72be6de 100644 --- a/pkg/provider/acme/provider_test.go +++ b/pkg/provider/acme/provider_test.go @@ -166,7 +166,6 @@ func TestGetUncheckedCertificates(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -241,7 +240,6 @@ func TestProvider_sanitizeDomains(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -423,7 +421,6 @@ func TestDeleteUnnecessaryDomains(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -497,7 +494,6 @@ func TestIsAccountMatchingCaServer(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -573,7 +569,6 @@ func TestInitAccount(t *testing.T) { }, } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -632,7 +627,6 @@ func Test_getCertificateRenewDurations(t *testing.T) { }, } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/aggregator/aggregator.go b/pkg/provider/aggregator/aggregator.go index 3ddb1c3f2..888d51fa0 100644 --- a/pkg/provider/aggregator/aggregator.go +++ b/pkg/provider/aggregator/aggregator.go @@ -183,7 +183,6 @@ func (p ProviderAggregator) Provide(configurationChan chan<- dynamic.Message, po } for _, prd := range p.providers { - prd := prd safe.Go(func() { p.launchProvider(configurationChan, pool, prd) }) diff --git a/pkg/provider/constraints/constraints_labels_test.go b/pkg/provider/constraints/constraints_labels_test.go index 43fb9e0c6..d17d40915 100644 --- a/pkg/provider/constraints/constraints_labels_test.go +++ b/pkg/provider/constraints/constraints_labels_test.go @@ -188,7 +188,6 @@ func TestMatchLabels(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.expr, func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/constraints/constraints_tags_test.go b/pkg/provider/constraints/constraints_tags_test.go index a563b0b30..531c715c3 100644 --- a/pkg/provider/constraints/constraints_tags_test.go +++ b/pkg/provider/constraints/constraints_tags_test.go @@ -95,7 +95,6 @@ func TestMatchTags(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.expr, func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/consulcatalog/config_test.go b/pkg/provider/consulcatalog/config_test.go index 9039f4697..5bfba067e 100644 --- a/pkg/provider/consulcatalog/config_test.go +++ b/pkg/provider/consulcatalog/config_test.go @@ -276,7 +276,6 @@ func TestDefaultRule(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -3161,8 +3160,6 @@ func Test_buildConfiguration(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -3232,8 +3229,6 @@ func TestNamespaces(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/consulcatalog/convert_types_test.go b/pkg/provider/consulcatalog/convert_types_test.go index 8dece14c2..e8ac70367 100644 --- a/pkg/provider/consulcatalog/convert_types_test.go +++ b/pkg/provider/consulcatalog/convert_types_test.go @@ -52,7 +52,6 @@ func Test_tagsToNeutralLabels(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/docker/config_test.go b/pkg/provider/docker/config_test.go index 0b835c6c4..3a9fe896f 100644 --- a/pkg/provider/docker/config_test.go +++ b/pkg/provider/docker/config_test.go @@ -369,7 +369,6 @@ func TestDefaultRule(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -3484,8 +3483,6 @@ func Test_buildConfiguration(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -3668,7 +3665,6 @@ func TestDockerGetIPPort(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -3745,7 +3741,6 @@ func TestDockerGetPort(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -3847,7 +3842,6 @@ func TestDockerGetIPAddress(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -3915,7 +3909,6 @@ func TestSwarmGetIPAddress(t *testing.T) { } for serviceID, test := range testCases { - test := test t.Run(strconv.Itoa(serviceID), func(t *testing.T) { t.Parallel() @@ -3950,7 +3943,6 @@ func TestSwarmGetPort(t *testing.T) { } for serviceID, test := range testCases { - test := test t.Run(strconv.Itoa(serviceID), func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/docker/docker.go b/pkg/provider/docker/docker.go index 0516620ce..5775b2785 100644 --- a/pkg/provider/docker/docker.go +++ b/pkg/provider/docker/docker.go @@ -456,8 +456,7 @@ func (p *Provider) listServices(ctx context.Context, dockerClient client.APIClie networkMap := make(map[string]*dockertypes.NetworkResource) for _, network := range networkList { - networkToAdd := network - networkMap[network.ID] = &networkToAdd + networkMap[network.ID] = &network } var dockerDataList []dockerData diff --git a/pkg/provider/docker/swarm_test.go b/pkg/provider/docker/swarm_test.go index 541af22f2..70c3aacee 100644 --- a/pkg/provider/docker/swarm_test.go +++ b/pkg/provider/docker/swarm_test.go @@ -79,7 +79,6 @@ func TestListTasks(t *testing.T) { } for caseID, test := range testCases { - test := test t.Run(strconv.Itoa(caseID), func(t *testing.T) { t.Parallel() @@ -271,7 +270,6 @@ func TestListServices(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -392,7 +390,6 @@ func TestSwarmTaskParsing(t *testing.T) { } for caseID, test := range testCases { - test := test t.Run(strconv.Itoa(caseID), func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/ecs/config_test.go b/pkg/provider/ecs/config_test.go index f00506094..3d8f912b8 100644 --- a/pkg/provider/ecs/config_test.go +++ b/pkg/provider/ecs/config_test.go @@ -340,7 +340,6 @@ func TestDefaultRule(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -3071,8 +3070,6 @@ func Test_buildConfiguration(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/ecs/ecs_test.go b/pkg/provider/ecs/ecs_test.go index ede115a41..b05bd2165 100644 --- a/pkg/provider/ecs/ecs_test.go +++ b/pkg/provider/ecs/ecs_test.go @@ -68,7 +68,6 @@ func TestChunkIDs(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go index 389b9a659..6724e5c63 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_test.go +++ b/pkg/provider/kubernetes/crd/kubernetes_test.go @@ -1422,8 +1422,6 @@ func TestLoadIngressRouteTCPs(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -4169,7 +4167,6 @@ func TestLoadIngressRoutes(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -4650,8 +4647,6 @@ func TestLoadIngressRouteUDPs(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -4727,8 +4722,6 @@ func TestParseServiceProtocol(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -4959,7 +4952,6 @@ func TestGetServicePort(t *testing.T) { }, } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -5931,8 +5923,6 @@ func TestCrossNamespace(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -6226,8 +6216,6 @@ func TestExternalNameService(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -6434,8 +6422,6 @@ func TestNativeLB(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/kubernetes/gateway/client_test.go b/pkg/provider/kubernetes/gateway/client_test.go index 794f7d6a8..069aa7628 100644 --- a/pkg/provider/kubernetes/gateway/client_test.go +++ b/pkg/provider/kubernetes/gateway/client_test.go @@ -233,8 +233,6 @@ func TestStatusEquals(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/kubernetes/gateway/kubernetes_test.go b/pkg/provider/kubernetes/gateway/kubernetes_test.go index 6fdfd82f0..ea0e7c504 100644 --- a/pkg/provider/kubernetes/gateway/kubernetes_test.go +++ b/pkg/provider/kubernetes/gateway/kubernetes_test.go @@ -1471,7 +1471,6 @@ func TestLoadHTTPRoutes(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -2206,7 +2205,6 @@ func TestLoadTCPRoutes(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -3299,7 +3297,6 @@ func TestLoadTLSRoutes(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -4251,7 +4248,6 @@ func TestLoadMixedRoutes(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -4361,7 +4357,6 @@ func Test_hostRule(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() rule, err := hostRule(test.hostnames) @@ -4592,7 +4587,6 @@ func Test_extractRule(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -4657,7 +4651,6 @@ func Test_hostSNIRule(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -4978,7 +4971,6 @@ func Test_shouldAttach(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -5082,7 +5074,6 @@ func Test_matchingHostnames(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -5194,7 +5185,6 @@ func Test_getAllowedRoutes(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -5240,7 +5230,6 @@ func Test_makeListenerKey(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/kubernetes/ingress/annotations_test.go b/pkg/provider/kubernetes/ingress/annotations_test.go index 45ef1cf78..7dbf309f9 100644 --- a/pkg/provider/kubernetes/ingress/annotations_test.go +++ b/pkg/provider/kubernetes/ingress/annotations_test.go @@ -80,7 +80,6 @@ func Test_parseRouterConfig(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -155,7 +154,6 @@ func Test_parseServiceConfig(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -239,7 +237,6 @@ func Test_convertAnnotations(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/kubernetes/ingress/client_test.go b/pkg/provider/kubernetes/ingress/client_test.go index 9ab39fdc6..725ba3491 100644 --- a/pkg/provider/kubernetes/ingress/client_test.go +++ b/pkg/provider/kubernetes/ingress/client_test.go @@ -47,7 +47,6 @@ func TestTranslateNotFoundError(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -125,7 +124,6 @@ func TestIsLoadBalancerIngressEquals(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/kubernetes/ingress/kubernetes_test.go b/pkg/provider/kubernetes/ingress/kubernetes_test.go index 329ca0735..abd08ab01 100644 --- a/pkg/provider/kubernetes/ingress/kubernetes_test.go +++ b/pkg/provider/kubernetes/ingress/kubernetes_test.go @@ -1602,8 +1602,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -1748,8 +1746,6 @@ func TestLoadConfigurationFromIngressesWithExternalNameServices(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -1828,8 +1824,6 @@ func TestLoadConfigurationFromIngressesWithNativeLB(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -2031,7 +2025,6 @@ func TestGetCertificates(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/kubernetes/k8s/event_handler_test.go b/pkg/provider/kubernetes/k8s/event_handler_test.go index e8b7a31a2..323b4f550 100644 --- a/pkg/provider/kubernetes/k8s/event_handler_test.go +++ b/pkg/provider/kubernetes/k8s/event_handler_test.go @@ -507,7 +507,6 @@ func Test_detectChanges(t *testing.T) { }, } for _, test := range tests { - test := test t.Run(test.name, func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/kv/consul/consul_test.go b/pkg/provider/kv/consul/consul_test.go index 286c0cd9c..765463cf3 100644 --- a/pkg/provider/kv/consul/consul_test.go +++ b/pkg/provider/kv/consul/consul_test.go @@ -35,8 +35,6 @@ func TestNamespaces(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/marathon/builder_test.go b/pkg/provider/marathon/builder_test.go index e18110787..4cf7a3468 100644 --- a/pkg/provider/marathon/builder_test.go +++ b/pkg/provider/marathon/builder_test.go @@ -121,8 +121,7 @@ func readinessCheckResult(taskID string, ready bool) func(*marathon.Application) func withTasks(tasks ...marathon.Task) func(*marathon.Application) { return func(application *marathon.Application) { for _, task := range tasks { - tu := task - application.Tasks = append(application.Tasks, &tu) + application.Tasks = append(application.Tasks, &task) } } } diff --git a/pkg/provider/marathon/config_test.go b/pkg/provider/marathon/config_test.go index 6e78830c5..8400b877a 100644 --- a/pkg/provider/marathon/config_test.go +++ b/pkg/provider/marathon/config_test.go @@ -2035,7 +2035,6 @@ func TestBuildConfiguration(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -2089,7 +2088,6 @@ func TestApplicationFilterEnabled(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -2492,7 +2490,6 @@ func TestGetServer(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/marathon/label_test.go b/pkg/provider/marathon/label_test.go index 472b91d23..1ce8901e4 100644 --- a/pkg/provider/marathon/label_test.go +++ b/pkg/provider/marathon/label_test.go @@ -123,7 +123,6 @@ func TestGetConfiguration(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/marathon/readiness_test.go b/pkg/provider/marathon/readiness_test.go index 8d61c01c4..86e063815 100644 --- a/pkg/provider/marathon/readiness_test.go +++ b/pkg/provider/marathon/readiness_test.go @@ -115,7 +115,6 @@ func TestEnabledReadinessChecker(t *testing.T) { } for _, test := range tests { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() rc := testReadinessChecker() diff --git a/pkg/provider/nomad/config_test.go b/pkg/provider/nomad/config_test.go index 9c964586b..84a4791bc 100644 --- a/pkg/provider/nomad/config_test.go +++ b/pkg/provider/nomad/config_test.go @@ -2765,8 +2765,6 @@ func TestNamespaces(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/nomad/nomad_test.go b/pkg/provider/nomad/nomad_test.go index f3fcd8894..745a85a23 100644 --- a/pkg/provider/nomad/nomad_test.go +++ b/pkg/provider/nomad/nomad_test.go @@ -118,7 +118,6 @@ func TestProvider_SetDefaults_Endpoint(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { for k, v := range test.envs { t.Setenv(k, v) diff --git a/pkg/provider/nomad/tag_test.go b/pkg/provider/nomad/tag_test.go index e15f5486c..69be3addb 100644 --- a/pkg/provider/nomad/tag_test.go +++ b/pkg/provider/nomad/tag_test.go @@ -97,7 +97,6 @@ func Test_tagsToLabels(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/rancher/config_test.go b/pkg/provider/rancher/config_test.go index dbed070c9..633e85415 100644 --- a/pkg/provider/rancher/config_test.go +++ b/pkg/provider/rancher/config_test.go @@ -1262,8 +1262,6 @@ func Test_buildConfiguration(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/provider/traefik/internal_test.go b/pkg/provider/traefik/internal_test.go index 1e7d5b282..133273044 100644 --- a/pkg/provider/traefik/internal_test.go +++ b/pkg/provider/traefik/internal_test.go @@ -257,7 +257,6 @@ func Test_createConfiguration(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/redactor/redactor_doOnJSON_test.go b/pkg/redactor/redactor_doOnJSON_test.go index ddae9c579..03e4082a1 100644 --- a/pkg/redactor/redactor_doOnJSON_test.go +++ b/pkg/redactor/redactor_doOnJSON_test.go @@ -59,7 +59,6 @@ func Test_doOnJSON_simple(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.name, func(t *testing.T) { t.Parallel() output := doOnJSON(test.input) diff --git a/pkg/redactor/redactor_doOnStruct_test.go b/pkg/redactor/redactor_doOnStruct_test.go index 96fe5edc9..af1f1faad 100644 --- a/pkg/redactor/redactor_doOnStruct_test.go +++ b/pkg/redactor/redactor_doOnStruct_test.go @@ -369,7 +369,6 @@ func Test_doOnStruct(t *testing.T) { } for _, test := range testCase { - test := test t.Run(test.name, func(t *testing.T) { t.Parallel() val := reflect.ValueOf(test.base).Elem() diff --git a/pkg/rules/parser.go b/pkg/rules/parser.go index 061895d3c..2555c01b9 100644 --- a/pkg/rules/parser.go +++ b/pkg/rules/parser.go @@ -31,7 +31,6 @@ func NewParser(matchers []string) (predicate.Parser, error) { parserFuncs := make(map[string]interface{}) for _, matcherName := range matchers { - matcherName := matcherName fn := func(value ...string) TreeBuilder { return func() *Tree { return &Tree{ diff --git a/pkg/rules/parser_test.go b/pkg/rules/parser_test.go index 503ab4298..b8aed5d5a 100644 --- a/pkg/rules/parser_test.go +++ b/pkg/rules/parser_test.go @@ -250,8 +250,6 @@ func TestRuleMatch(t *testing.T) { require.NoError(t, err) for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/safe/routine_test.go b/pkg/safe/routine_test.go index 1880b7c4e..12875972c 100644 --- a/pkg/safe/routine_test.go +++ b/pkg/safe/routine_test.go @@ -63,7 +63,6 @@ func TestPoolWithCtx(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { // These subtests cannot be run in parallel, since the testRoutine // is shared across the subtests. diff --git a/pkg/server/aggregator_test.go b/pkg/server/aggregator_test.go index cc4557896..48ba181a2 100644 --- a/pkg/server/aggregator_test.go +++ b/pkg/server/aggregator_test.go @@ -113,7 +113,6 @@ func Test_mergeConfiguration(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -162,7 +161,6 @@ func Test_mergeConfiguration_tlsCertificates(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -346,8 +344,6 @@ func Test_mergeConfiguration_tlsOptions(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -439,7 +435,6 @@ func Test_mergeConfiguration_tlsStore(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -662,7 +657,6 @@ func Test_applyModel(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/server/cookie/cookie_test.go b/pkg/server/cookie/cookie_test.go index 125e9dce1..452f0f221 100644 --- a/pkg/server/cookie/cookie_test.go +++ b/pkg/server/cookie/cookie_test.go @@ -34,7 +34,6 @@ func TestGetName(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -64,7 +63,6 @@ func Test_sanitizeName(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/server/middleware/middlewares_test.go b/pkg/server/middleware/middlewares_test.go index 5d8db1aa8..58b053053 100644 --- a/pkg/server/middleware/middlewares_test.go +++ b/pkg/server/middleware/middlewares_test.go @@ -256,7 +256,6 @@ func TestBuilder_BuildChainWithContext(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -364,7 +363,6 @@ func TestBuilder_buildConstructor(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/server/provider/provider_test.go b/pkg/server/provider/provider_test.go index dc229e532..7686d1bb5 100644 --- a/pkg/server/provider/provider_test.go +++ b/pkg/server/provider/provider_test.go @@ -47,7 +47,6 @@ func TestAddInContext(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -103,7 +102,6 @@ func TestGetQualifiedName(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/server/router/router.go b/pkg/server/router/router.go index e8f214ffb..547066ddc 100644 --- a/pkg/server/router/router.go +++ b/pkg/server/router/router.go @@ -67,7 +67,6 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string, t entryPointHandlers := make(map[string]http.Handler) for entryPointName, routers := range m.getHTTPRouters(rootCtx, entryPoints, tls) { - entryPointName := entryPointName ctx := log.With(rootCtx, log.Str(log.EntryPointName, entryPointName)) handler, err := m.buildEntryPointHandler(ctx, routers) diff --git a/pkg/server/router/router_test.go b/pkg/server/router/router_test.go index 65d074039..3cce3c4c4 100644 --- a/pkg/server/router/router_test.go +++ b/pkg/server/router/router_test.go @@ -301,7 +301,6 @@ func TestRouterManager_Get(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -765,7 +764,6 @@ func TestRuntimeConfiguration(t *testing.T) { }, } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/server/router/tcp/manager.go b/pkg/server/router/tcp/manager.go index b149f5f0b..5b6e18385 100644 --- a/pkg/server/router/tcp/manager.go +++ b/pkg/server/router/tcp/manager.go @@ -73,8 +73,6 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string) m entryPointHandlers := make(map[string]*Router) for _, entryPointName := range entryPoints { - entryPointName := entryPointName - routers := entryPointsRouters[entryPointName] ctx := log.With(rootCtx, log.Str(log.EntryPointName, entryPointName)) diff --git a/pkg/server/router/tcp/manager_test.go b/pkg/server/router/tcp/manager_test.go index dd31ce3b3..0b6066b8a 100644 --- a/pkg/server/router/tcp/manager_test.go +++ b/pkg/server/router/tcp/manager_test.go @@ -298,8 +298,6 @@ func TestRuntimeConfiguration(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/server/router/tcp/router_test.go b/pkg/server/router/tcp/router_test.go index 89d8c11ff..70f23b12c 100644 --- a/pkg/server/router/tcp/router_test.go +++ b/pkg/server/router/tcp/router_test.go @@ -539,8 +539,6 @@ func Test_Routing(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/server/router/udp/router.go b/pkg/server/router/udp/router.go index 75bea883a..dedea33e9 100644 --- a/pkg/server/router/udp/router.go +++ b/pkg/server/router/udp/router.go @@ -42,8 +42,6 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string) m entryPointHandlers := make(map[string]udp.Handler) for _, entryPointName := range entryPoints { - entryPointName := entryPointName - routers := entryPointsRouters[entryPointName] ctx := log.With(rootCtx, log.Str(log.EntryPointName, entryPointName)) diff --git a/pkg/server/router/udp/router_test.go b/pkg/server/router/udp/router_test.go index d29d7d2d6..a71662b48 100644 --- a/pkg/server/router/udp/router_test.go +++ b/pkg/server/router/udp/router_test.go @@ -106,8 +106,6 @@ func TestRuntimeConfiguration(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/server/routerfactory_test.go b/pkg/server/routerfactory_test.go index fdf4efc69..f70b62933 100644 --- a/pkg/server/routerfactory_test.go +++ b/pkg/server/routerfactory_test.go @@ -168,8 +168,6 @@ func TestServerResponseEmptyBackend(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/server/service/roundtripper_test.go b/pkg/server/service/roundtripper_test.go index d9212ab8d..8171de56b 100644 --- a/pkg/server/service/roundtripper_test.go +++ b/pkg/server/service/roundtripper_test.go @@ -259,7 +259,6 @@ func TestDisableHTTP2(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -333,7 +332,6 @@ func TestKerberosRoundTripper(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/server/service/service_test.go b/pkg/server/service/service_test.go index 0ff7e86ae..b761b410e 100644 --- a/pkg/server/service/service_test.go +++ b/pkg/server/service/service_test.go @@ -66,7 +66,6 @@ func TestGetLoadBalancer(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -336,7 +335,6 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { handler, err := sm.getLoadBalancerServiceHandler(context.Background(), test.serviceName, test.service) @@ -522,7 +520,6 @@ func TestManager_Build(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/server/service/tcp/service_test.go b/pkg/server/service/tcp/service_test.go index fc6c4702c..6587469cc 100644 --- a/pkg/server/service/tcp/service_test.go +++ b/pkg/server/service/tcp/service_test.go @@ -174,7 +174,6 @@ func TestManager_BuildTCP(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/server/service/udp/service_test.go b/pkg/server/service/udp/service_test.go index 0f06c1feb..a3ee3b15e 100644 --- a/pkg/server/service/udp/service_test.go +++ b/pkg/server/service/udp/service_test.go @@ -174,7 +174,6 @@ func TestManager_BuildUDP(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/tcp/proxy_test.go b/pkg/tcp/proxy_test.go index fb8cad1dd..feddb80fa 100644 --- a/pkg/tcp/proxy_test.go +++ b/pkg/tcp/proxy_test.go @@ -104,8 +104,6 @@ func TestProxyProtocol(t *testing.T) { } for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { backendListener, err := net.Listen("tcp", ":0") require.NoError(t, err) @@ -194,7 +192,6 @@ func TestLookupAddress(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/tcp/wrr_load_balancer_test.go b/pkg/tcp/wrr_load_balancer_test.go index 5ee18caad..fc3438ac6 100644 --- a/pkg/tcp/wrr_load_balancer_test.go +++ b/pkg/tcp/wrr_load_balancer_test.go @@ -121,13 +121,11 @@ func TestLoadBalancing(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() balancer := NewWRRLoadBalancer() for server, weight := range test.serversWeight { - server := server balancer.AddWeightServer(HandlerFunc(func(conn WriteCloser) { _, err := conn.Write([]byte(server)) require.NoError(t, err) diff --git a/pkg/tls/certificate_store_test.go b/pkg/tls/certificate_store_test.go index a80516142..aaf55b647 100644 --- a/pkg/tls/certificate_store_test.go +++ b/pkg/tls/certificate_store_test.go @@ -56,7 +56,6 @@ func TestGetBestCertificate(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() dynamicMap := map[string]*tls.Certificate{} diff --git a/pkg/tls/tlsmanager_test.go b/pkg/tls/tlsmanager_test.go index 644a80d03..c91e244a1 100644 --- a/pkg/tls/tlsmanager_test.go +++ b/pkg/tls/tlsmanager_test.go @@ -161,7 +161,6 @@ func TestManager_Get(t *testing.T) { tlsManager.UpdateConfigs(context.Background(), nil, tlsConfigs, dynamicConfigs) for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -301,7 +300,6 @@ func TestClientAuth(t *testing.T) { tlsManager.UpdateConfigs(context.Background(), nil, tlsConfigs, nil) for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/tracing/operation_name_test.go b/pkg/tracing/operation_name_test.go index daae56806..df29f0dbf 100644 --- a/pkg/tracing/operation_name_test.go +++ b/pkg/tracing/operation_name_test.go @@ -54,7 +54,6 @@ func Test_generateOperationName(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -83,7 +82,6 @@ func TestComputeHash(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -122,7 +120,6 @@ func TestTruncateString(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/types/domain_test.go b/pkg/types/domain_test.go index dc97c7971..f9f7d42cb 100644 --- a/pkg/types/domain_test.go +++ b/pkg/types/domain_test.go @@ -37,7 +37,6 @@ func TestDomain_ToStrArray(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -77,7 +76,6 @@ func TestDomain_Set(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -171,7 +169,6 @@ func TestMatchDomain(t *testing.T) { } for _, test := range testCases { - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff --git a/pkg/types/tls_test.go b/pkg/types/tls_test.go index d3e93f408..b123aba53 100644 --- a/pkg/types/tls_test.go +++ b/pkg/types/tls_test.go @@ -104,8 +104,6 @@ func TestClientTLS_CreateTLSConfig(t *testing.T) { } for _, test := range tests { - test := test - t.Run(test.desc, func(t *testing.T) { tlsConfig, err := test.clientTLS.CreateTLSConfig(context.Background()) if test.wantErr { From d94e67608391b45bb6b8717fb8b06cd8064acc1c Mon Sep 17 00:00:00 2001 From: Baptiste Mayelle Date: Mon, 25 Mar 2024 11:08:04 +0100 Subject: [PATCH 05/16] Enforce failure for TCP HostSNI with hostname Co-authored-by: Romain --- integration/fixtures/tcp/service_errors.toml | 2 ++ pkg/server/router/tcp/manager.go | 1 + pkg/server/router/tcp/manager_test.go | 1 + 3 files changed, 4 insertions(+) diff --git a/integration/fixtures/tcp/service_errors.toml b/integration/fixtures/tcp/service_errors.toml index b6949da71..1954f720d 100644 --- a/integration/fixtures/tcp/service_errors.toml +++ b/integration/fixtures/tcp/service_errors.toml @@ -23,10 +23,12 @@ [tcp.routers.router1] service = "service1" rule = "HostSNI(`snitest.net`)" + [tcp.routers.router1.tls] [tcp.routers.router2] service = "service2" rule = "HostSNI(`snitest.com`)" + [tcp.routers.router2.tls] [tcp.services] [tcp.services.service1] diff --git a/pkg/server/router/tcp/manager.go b/pkg/server/router/tcp/manager.go index 5b6e18385..abc5d1c64 100644 --- a/pkg/server/router/tcp/manager.go +++ b/pkg/server/router/tcp/manager.go @@ -288,6 +288,7 @@ func (m *Manager) addTCPHandlers(ctx context.Context, configs map[string]*runtim routerErr := fmt.Errorf("invalid rule: %q , has HostSNI matcher, but no TLS on router", routerConfig.Rule) routerConfig.AddError(routerErr, true) logger.Error(routerErr) + continue } var handler tcp.Handler diff --git a/pkg/server/router/tcp/manager_test.go b/pkg/server/router/tcp/manager_test.go index 0b6066b8a..219eb90d8 100644 --- a/pkg/server/router/tcp/manager_test.go +++ b/pkg/server/router/tcp/manager_test.go @@ -264,6 +264,7 @@ func TestRuntimeConfiguration(t *testing.T) { EntryPoints: []string{"web"}, Service: "foo-service", Rule: "HostSNI(`foo.bar`)", + TLS: &dynamic.RouterTCPTLSConfig{}, }, }, }, From 3fcf265d8000cc80ef0e2ffafe176ce2bf376b7d Mon Sep 17 00:00:00 2001 From: arukiidou Date: Tue, 26 Mar 2024 04:22:05 +0900 Subject: [PATCH 06/16] Move from http.FileServer to http.FileServerFS --- pkg/api/dashboard/dashboard.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/api/dashboard/dashboard.go b/pkg/api/dashboard/dashboard.go index ebcaf315e..667092dbd 100644 --- a/pkg/api/dashboard/dashboard.go +++ b/pkg/api/dashboard/dashboard.go @@ -34,7 +34,7 @@ func Append(router *mux.Router, customAssets fs.FS) { // allow iframes from our domains only // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-src w.Header().Set("Content-Security-Policy", "frame-src 'self' https://traefik.io https://*.traefik.io;") - http.StripPrefix("/dashboard/", http.FileServer(http.FS(assets))).ServeHTTP(w, r) + http.StripPrefix("/dashboard/", http.FileServerFS(assets)).ServeHTTP(w, r) }) } @@ -46,7 +46,7 @@ func (g Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // allow iframes from our domains only // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-src w.Header().Set("Content-Security-Policy", "frame-src 'self' https://traefik.io https://*.traefik.io;") - http.FileServer(http.FS(assets)).ServeHTTP(w, r) + http.FileServerFS(assets).ServeHTTP(w, r) } func safePrefix(req *http.Request) string { From 7f29595c0a5d8f824757b624f0774d12c4f9743e Mon Sep 17 00:00:00 2001 From: Romain Date: Tue, 26 Mar 2024 13:28:04 +0100 Subject: [PATCH 07/16] Allow empty replacement with ReplacePathRegex middleware --- .../replacepathregex/replace_path_regex.go | 2 +- .../replace_path_regex_test.go | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/pkg/middlewares/replacepathregex/replace_path_regex.go b/pkg/middlewares/replacepathregex/replace_path_regex.go index fea6fa2de..fb2c47189 100644 --- a/pkg/middlewares/replacepathregex/replace_path_regex.go +++ b/pkg/middlewares/replacepathregex/replace_path_regex.go @@ -53,7 +53,7 @@ func (rp *replacePathRegex) ServeHTTP(rw http.ResponseWriter, req *http.Request) currentPath = req.URL.EscapedPath() } - if rp.regexp != nil && len(rp.replacement) > 0 && rp.regexp.MatchString(currentPath) { + if rp.regexp != nil && rp.regexp.MatchString(currentPath) { req.Header.Add(replacepath.ReplacedPathHeader, currentPath) req.URL.RawPath = rp.regexp.ReplaceAllString(currentPath, rp.replacement) diff --git a/pkg/middlewares/replacepathregex/replace_path_regex_test.go b/pkg/middlewares/replacepathregex/replace_path_regex_test.go index 59018d784..b7e637851 100644 --- a/pkg/middlewares/replacepathregex/replace_path_regex_test.go +++ b/pkg/middlewares/replacepathregex/replace_path_regex_test.go @@ -44,6 +44,28 @@ func TestReplacePathRegex(t *testing.T) { expectedRawPath: "/who-am-i/and/who-am-i", expectedHeader: "/whoami/and/whoami", }, + { + desc: "empty replacement", + path: "/whoami/and/whoami", + config: dynamic.ReplacePathRegex{ + Replacement: "", + Regex: `/whoami`, + }, + expectedPath: "/and", + expectedRawPath: "/and", + expectedHeader: "/whoami/and/whoami", + }, + { + desc: "empty trimmed replacement", + path: "/whoami/and/whoami", + config: dynamic.ReplacePathRegex{ + Replacement: " ", + Regex: `/whoami`, + }, + expectedPath: "/and", + expectedRawPath: "/and", + expectedHeader: "/whoami/and/whoami", + }, { desc: "no match", path: "/whoami/and/whoami", From 167bdb0d53b0e68530f38b43329e10705d72cbe4 Mon Sep 17 00:00:00 2001 From: Michel Loiseleur <97035654+mloiseleur@users.noreply.github.com> Date: Thu, 28 Mar 2024 14:36:04 +0100 Subject: [PATCH 08/16] docs: improve middleware example --- docs/content/routing/providers/kubernetes-crd.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/routing/providers/kubernetes-crd.md b/docs/content/routing/providers/kubernetes-crd.md index 98963bd09..17cb204db 100644 --- a/docs/content/routing/providers/kubernetes-crd.md +++ b/docs/content/routing/providers/kubernetes-crd.md @@ -371,7 +371,7 @@ Register the `IngressRoute` [kind](../../reference/dynamic-configuration/kuberne | [4] | `routes[n].priority` | Defines the [priority](../routers/index.md#priority) to disambiguate rules of the same length, for route matching | | [5] | `routes[n].middlewares` | List of reference to [Middleware](#kind-middleware) | | [6] | `middlewares[n].name` | Defines the [Middleware](#kind-middleware) name | -| [7] | `middlewares[n].namespace` | Defines the [Middleware](#kind-middleware) namespace | +| [7] | `middlewares[n].namespace` | Defines the [Middleware](#kind-middleware) namespace. It can be omitted when the Middleware is in the IngressRoute namespace. | | [8] | `routes[n].services` | List of any combination of [TraefikService](#kind-traefikservice) and reference to a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) (See below for `ExternalName Service` setup) | | [9] | `services[n].port` | Defines the port of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/). This can be a reference to a named port. | | [10] | `services[n].serversTransport` | Defines the reference to a [ServersTransport](#kind-serverstransport). The ServersTransport namespace is assumed to be the [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) namespace (see [ServersTransport reference](#serverstransport-reference)). | From c31f5df85446f2998c2e8d363a4940d53fcecc7d Mon Sep 17 00:00:00 2001 From: Romain Date: Fri, 29 Mar 2024 11:36:05 +0100 Subject: [PATCH 09/16] Enforce handling of ACME-TLS/1 challenges Co-authored-by: Baptiste Mayelle Co-authored-by: Kevin Pollet --- pkg/server/router/tcp/router.go | 19 ++++++++ pkg/server/router/tcp/router_test.go | 70 +++++++++++++++++++++++++++- 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/pkg/server/router/tcp/router.go b/pkg/server/router/tcp/router.go index dca4e6fae..0d8524398 100644 --- a/pkg/server/router/tcp/router.go +++ b/pkg/server/router/tcp/router.go @@ -8,8 +8,10 @@ import ( "io" "net" "net/http" + "slices" "time" + "github.com/go-acme/lego/v4/challenge/tlsalpn01" "github.com/traefik/traefik/v2/pkg/log" tcpmuxer "github.com/traefik/traefik/v2/pkg/muxer/tcp" "github.com/traefik/traefik/v2/pkg/tcp" @@ -146,6 +148,12 @@ func (r *Router) ServeTCP(conn tcp.WriteCloser) { return } + // Handling ACME-TLS/1 challenges. + if slices.Contains(hello.protos, tlsalpn01.ACMETLS1Protocol) { + r.acmeTLSALPNHandler().ServeTCP(r.GetConn(conn, hello.peeked)) + return + } + // For real, the handler eventually used for HTTPS is (almost) always the same: // it is the httpsForwarder that is used for all HTTPS connections that match // (which is also incidentally the same used in the last block below for 404s). @@ -190,6 +198,17 @@ func (r *Router) ServeTCP(conn tcp.WriteCloser) { conn.Close() } +// acmeTLSALPNHandler returns a special handler to solve ACME-TLS/1 challenges. +func (r *Router) acmeTLSALPNHandler() tcp.Handler { + if r.httpsTLSConfig == nil { + return &brokenTLSRouter{} + } + + return tcp.HandlerFunc(func(conn tcp.WriteCloser) { + _ = tls.Server(conn, r.httpsTLSConfig).Handshake() + }) +} + // AddRoute defines a handler for the given rule. func (r *Router) AddRoute(rule string, priority int, target tcp.Handler) error { return r.muxerTCP.AddRoute(rule, priority, target) diff --git a/pkg/server/router/tcp/router_test.go b/pkg/server/router/tcp/router_test.go index 70f23b12c..8f6ae37c1 100644 --- a/pkg/server/router/tcp/router_test.go +++ b/pkg/server/router/tcp/router_test.go @@ -14,6 +14,7 @@ import ( "testing" "time" + "github.com/go-acme/lego/v4/challenge/tlsalpn01" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/traefik/traefik/v2/pkg/config/dynamic" @@ -22,6 +23,7 @@ import ( "github.com/traefik/traefik/v2/pkg/server/service/tcp" tcp2 "github.com/traefik/traefik/v2/pkg/tcp" traefiktls "github.com/traefik/traefik/v2/pkg/tls" + "github.com/traefik/traefik/v2/pkg/tls/generate" ) type applyRouter func(conf *runtime.Configuration) @@ -164,11 +166,16 @@ func Test_Routing(t *testing.T) { serviceManager := tcp.NewManager(conf) + certPEM, keyPEM, err := generate.KeyPair("foo.bar", time.Time{}) + require.NoError(t, err) + // Creates the tlsManager and defines the TLS 1.0 and 1.2 TLSOptions. tlsManager := traefiktls.NewManager() tlsManager.UpdateConfigs( context.Background(), - map[string]traefiktls.Store{}, + map[string]traefiktls.Store{ + tlsalpn01.ACMETLS1Protocol: {}, + }, map[string]traefiktls.Options{ "default": { MinVersion: "VersionTLS10", @@ -183,7 +190,10 @@ func Test_Routing(t *testing.T) { MaxVersion: "VersionTLS12", }, }, - []*traefiktls.CertAndStores{}) + []*traefiktls.CertAndStores{{ + Certificate: traefiktls.Certificate{CertFile: traefiktls.FileOrContent(certPEM), KeyFile: traefiktls.FileOrContent(keyPEM)}, + Stores: []string{tlsalpn01.ACMETLS1Protocol}, + }}) middlewaresBuilder := tcpmiddleware.NewBuilder(conf.TCPMiddlewares) @@ -207,6 +217,10 @@ func Test_Routing(t *testing.T) { desc: "No routers", routers: []applyRouter{}, checks: []checkCase{ + { + desc: "ACME TLS Challenge", + checkRouter: checkACMETLS, + }, { desc: "TCP with client sending first bytes should fail", checkRouter: checkTCPClientFirst, @@ -244,6 +258,16 @@ func Test_Routing(t *testing.T) { }, }, }, + { + desc: "TCP TLS passthrough does not catch ACME TLS", + routers: []applyRouter{routerTCPTLSCatchAllPassthrough}, + checks: []checkCase{ + { + desc: "ACME TLS Challenge", + checkRouter: checkACMETLS, + }, + }, + }, { desc: "Single TCP CatchAll router", routers: []applyRouter{routerTCPCatchAll}, @@ -675,6 +699,21 @@ func routerTCPTLSCatchAll(conf *runtime.Configuration) { } } +// routerTCPTLSCatchAllPassthrough a TCP TLS CatchAll Passthrough - HostSNI(`*`) router with TLS 1.0 config. +func routerTCPTLSCatchAllPassthrough(conf *runtime.Configuration) { + conf.TCPRouters["tcp-tls-catchall-passthrough"] = &runtime.TCPRouterInfo{ + TCPRouter: &dynamic.TCPRouter{ + EntryPoints: []string{"web"}, + Service: "tcp", + Rule: "HostSNI(`*`)", + TLS: &dynamic.RouterTCPTLSConfig{ + Options: "tls12", + Passthrough: true, + }, + }, + } +} + // routerTCPTLS configures a TCP TLS - HostSNI(`foo.bar`) router with TLS 1.2 config. func routerTCPTLS(conf *runtime.Configuration) { conf.TCPRouters["tcp-tls"] = &runtime.TCPRouterInfo{ @@ -717,6 +756,33 @@ func routerHTTPS(conf *runtime.Configuration) { } } +// checkACMETLS simulates a ACME TLS Challenge client connection. +// It returns an error if TLS handshake fails. +func checkACMETLS(addr string, _ time.Duration) (err error) { + tlsConfig := &tls.Config{ + InsecureSkipVerify: true, + ServerName: "foo.bar", + MinVersion: tls.VersionTLS10, + NextProtos: []string{tlsalpn01.ACMETLS1Protocol}, + } + conn, err := tls.Dial("tcp", addr, tlsConfig) + if err != nil { + return err + } + defer func() { + closeErr := conn.Close() + if closeErr != nil && err == nil { + err = closeErr + } + }() + + if conn.ConnectionState().Version != tls.VersionTLS10 { + return fmt.Errorf("wrong TLS version. wanted %X, got %X", tls.VersionTLS10, conn.ConnectionState().Version) + } + + return nil +} + // checkTCPClientFirst simulates a TCP client sending first bytes first. // It returns an error if it doesn't receive the expected response. func checkTCPClientFirst(addr string, timeout time.Duration) (err error) { From fc897f6756a3a6ae9e3acd604a71ae4f4a675245 Mon Sep 17 00:00:00 2001 From: Baptiste Mayelle Date: Tue, 2 Apr 2024 14:46:04 +0200 Subject: [PATCH 10/16] fix: support regexp in path/pathprefix in matcher v2 --- pkg/muxer/http/matcher_v2.go | 28 ++++++++++++++++++++-------- pkg/muxer/http/matcher_v2_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/pkg/muxer/http/matcher_v2.go b/pkg/muxer/http/matcher_v2.go index 72d5d826a..d87b8e41d 100644 --- a/pkg/muxer/http/matcher_v2.go +++ b/pkg/muxer/http/matcher_v2.go @@ -25,15 +25,21 @@ var httpFuncsV2 = map[string]func(*matchersTree, ...string) error{ } func pathV2(tree *matchersTree, paths ...string) error { + var routes []*mux.Route + for _, path := range paths { - if !strings.HasPrefix(path, "/") { - return fmt.Errorf("path %q does not start with a '/'", path) + route := mux.NewRouter().NewRoute() + + if err := route.Path(path).GetError(); err != nil { + return err } + + routes = append(routes, route) } tree.matcher = func(req *http.Request) bool { - for _, path := range paths { - if req.URL.Path == path { + for _, route := range routes { + if route.Match(req, &mux.RouteMatch{}) { return true } } @@ -45,15 +51,21 @@ func pathV2(tree *matchersTree, paths ...string) error { } func pathPrefixV2(tree *matchersTree, paths ...string) error { + var routes []*mux.Route + for _, path := range paths { - if !strings.HasPrefix(path, "/") { - return fmt.Errorf("path %q does not start with a '/'", path) + route := mux.NewRouter().NewRoute() + + if err := route.PathPrefix(path).GetError(); err != nil { + return err } + + routes = append(routes, route) } tree.matcher = func(req *http.Request) bool { - for _, path := range paths { - if strings.HasPrefix(req.URL.Path, path) { + for _, route := range routes { + if route.Match(req, &mux.RouteMatch{}) { return true } } diff --git a/pkg/muxer/http/matcher_v2_test.go b/pkg/muxer/http/matcher_v2_test.go index eac62a005..f8c381712 100644 --- a/pkg/muxer/http/matcher_v2_test.go +++ b/pkg/muxer/http/matcher_v2_test.go @@ -454,6 +454,18 @@ func TestPathV2Matcher(t *testing.T) { "https://example.com/css/main.css": http.StatusNotFound, }, }, + { + desc: "valid Path matcher with regexp", + rule: "Path(`/css{path:(/.*)?}`)", + expected: map[string]int{ + "https://example.com": http.StatusNotFound, + "https://example.com/css/main.css": http.StatusOK, + "https://example.org/css/main.css": http.StatusOK, + "https://example.com/css/components/component.css": http.StatusOK, + "https://example.com/css.css": http.StatusNotFound, + "https://example.com/js/main.js": http.StatusNotFound, + }, + }, } for _, test := range testCases { @@ -535,6 +547,18 @@ func TestPathPrefixV2Matcher(t *testing.T) { "https://example.com/css/main.css": http.StatusOK, }, }, + { + desc: "valid PathPrefix matcher with regexp", + rule: "PathPrefix(`/css-{name:[0-9]?}`)", + expected: map[string]int{ + "https://example.com": http.StatusNotFound, + "https://example.com/css-1/main.css": http.StatusOK, + "https://example.org/css-222/main.css": http.StatusOK, + "https://example.com/css-333333/components/component.css": http.StatusOK, + "https://example.com/css.css": http.StatusNotFound, + "https://example.com/js/main.js": http.StatusNotFound, + }, + }, } for _, test := range testCases { From 2bc3fa7b4bfa627e6e9c7f66a483b4c826f39e88 Mon Sep 17 00:00:00 2001 From: Baptiste Mayelle Date: Tue, 2 Apr 2024 17:04:05 +0200 Subject: [PATCH 11/16] Reserve priority range for internal routers Co-authored-by: Romain --- docs/content/migration/v2.md | 17 ++++++++++++++ docs/content/routing/routers/index.md | 16 +++++++++++++ pkg/server/router/router.go | 11 +++++++++ pkg/server/router/router_test.go | 27 +++++++++++++++++++++ pkg/server/router/tcp/manager.go | 11 +++++++++ pkg/server/router/tcp/manager_test.go | 34 +++++++++++++++++++++++++++ 6 files changed, 116 insertions(+) diff --git a/docs/content/migration/v2.md b/docs/content/migration/v2.md index 556bad2d0..0f3114fc3 100644 --- a/docs/content/migration/v2.md +++ b/docs/content/migration/v2.md @@ -560,3 +560,20 @@ To enable these ciphers, please set the option `CipherSuites` in your [TLS confi > (https://go.dev/doc/go1.22#crypto/tls) To enable TLS 1.0, please set the option `MinVersion` to `VersionTLS10` in your [TLS configuration](https://doc.traefik.io/traefik/https/tls/#cipher-suites) or set the environment variable `GODEBUG=tls10server=1`. + +## v2.11.1 + +### Maximum Router Priority Value + +Before v2.11.1, the maximum user-defined router priority value is: + +- `MaxInt32` for 32-bit platforms, +- `MaxInt64` for 64-bit platforms. + +Please check out the [go documentation](https://pkg.go.dev/math#pkg-constants) for more information. + +In v2.11.1, Traefik reserves a range of priorities for its internal routers and now, +the maximum user-defined router priority value is: + +- `(MaxInt32 - 1000)` for 32-bit platforms, +- `(MaxInt64 - 1000)` for 64-bit platforms. diff --git a/docs/content/routing/routers/index.md b/docs/content/routing/routers/index.md index 6fedaa945..418f77201 100644 --- a/docs/content/routing/routers/index.md +++ b/docs/content/routing/routers/index.md @@ -293,6 +293,14 @@ To avoid path overlap, routes are sorted, by default, in descending order using A value of `0` for the priority is ignored: `priority = 0` means that the default rules length sorting is used. +??? warning "Maximum Value" + + Traefik reserves a range of priorities for its internal routers, + the maximum user-defined router priority value is: + + - `(MaxInt32 - 1000)` for 32-bit platforms, + - `(MaxInt64 - 1000)` for 64-bit platforms. + ??? info "How default priorities are computed" ```yaml tab="File (YAML)" @@ -897,6 +905,14 @@ The priority is directly equal to the length of the rule, and so the longest len A value of `0` for the priority is ignored: `priority = 0` means that the default rules length sorting is used. +??? warning "Maximum Value" + + Traefik reserves a range of priorities for its internal routers, + the maximum user-defined router priority value is: + + - `(MaxInt32 - 1000)` for 32-bit platforms, + - `(MaxInt64 - 1000)` for 64-bit platforms. + ??? info "How default priorities are computed" ```yaml tab="File (YAML)" diff --git a/pkg/server/router/router.go b/pkg/server/router/router.go index 547066ddc..0b88b0faf 100644 --- a/pkg/server/router/router.go +++ b/pkg/server/router/router.go @@ -4,7 +4,9 @@ import ( "context" "errors" "fmt" + "math" "net/http" + "strings" "github.com/containous/alice" "github.com/traefik/traefik/v2/pkg/config/runtime" @@ -21,6 +23,8 @@ import ( "github.com/traefik/traefik/v2/pkg/tls" ) +const maxUserPriority = math.MaxInt - 1000 + type middlewareBuilder interface { BuildChain(ctx context.Context, names []string) *alice.Chain } @@ -115,6 +119,13 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string ctxRouter := log.With(provider.AddInContext(ctx, routerName), log.Str(log.RouterName, routerName)) logger := log.FromContext(ctxRouter) + if routerConfig.Priority > maxUserPriority && !strings.HasSuffix(routerName, "@internal") { + err = fmt.Errorf("the router priority %d exceeds the max user-defined priority %d", routerConfig.Priority, maxUserPriority) + routerConfig.AddError(err, true) + logger.Error(err) + continue + } + handler, err := m.buildRouterHandler(ctxRouter, routerName, routerConfig) if err != nil { routerConfig.AddError(err, true) diff --git a/pkg/server/router/router_test.go b/pkg/server/router/router_test.go index 3cce3c4c4..d5fba5af1 100644 --- a/pkg/server/router/router_test.go +++ b/pkg/server/router/router_test.go @@ -3,6 +3,7 @@ package router import ( "context" "io" + "math" "net/http" "net/http/httptest" "strings" @@ -698,6 +699,32 @@ func TestRuntimeConfiguration(t *testing.T) { }, expectedError: 2, }, + { + desc: "Router priority exceeding max user-defined priority", + serviceConfig: map[string]*dynamic.Service{ + "foo-service": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1", + }, + }, + }, + }, + }, + middlewareConfig: map[string]*dynamic.Middleware{}, + routerConfig: map[string]*dynamic.Router{ + "bar": { + EntryPoints: []string{"web"}, + Service: "foo-service", + Rule: "Host(`foo.bar`)", + Priority: math.MaxInt, + TLS: &dynamic.RouterTLSConfig{}, + }, + }, + tlsOptions: map[string]tls.Options{}, + expectedError: 1, + }, { desc: "Router with broken tlsOption", serviceConfig: map[string]*dynamic.Service{ diff --git a/pkg/server/router/tcp/manager.go b/pkg/server/router/tcp/manager.go index abc5d1c64..45007fbd8 100644 --- a/pkg/server/router/tcp/manager.go +++ b/pkg/server/router/tcp/manager.go @@ -5,7 +5,9 @@ import ( "crypto/tls" "errors" "fmt" + "math" "net/http" + "strings" "github.com/traefik/traefik/v2/pkg/config/runtime" "github.com/traefik/traefik/v2/pkg/log" @@ -18,6 +20,8 @@ import ( traefiktls "github.com/traefik/traefik/v2/pkg/tls" ) +const maxUserPriority = math.MaxInt - 1000 + type middlewareBuilder interface { BuildChain(ctx context.Context, names []string) *tcp.Chain } @@ -291,6 +295,13 @@ func (m *Manager) addTCPHandlers(ctx context.Context, configs map[string]*runtim continue } + if routerConfig.Priority > maxUserPriority && !strings.HasSuffix(routerName, "@internal") { + routerErr := fmt.Errorf("the router priority %d exceeds the max user-defined priority %d", routerConfig.Priority, maxUserPriority) + routerConfig.AddError(routerErr, true) + logger.Error(routerErr) + continue + } + var handler tcp.Handler if routerConfig.TLS == nil || routerConfig.TLS.Passthrough { handler, err = m.buildTCPHandler(ctxRouter, routerConfig) diff --git a/pkg/server/router/tcp/manager_test.go b/pkg/server/router/tcp/manager_test.go index 219eb90d8..2ccbcb24b 100644 --- a/pkg/server/router/tcp/manager_test.go +++ b/pkg/server/router/tcp/manager_test.go @@ -3,6 +3,7 @@ package tcp import ( "context" "crypto/tls" + "math" "net/http" "net/http/httptest" "testing" @@ -270,6 +271,39 @@ func TestRuntimeConfiguration(t *testing.T) { }, expectedError: 2, }, + { + desc: "Router with priority exceeding the max user-defined priority", + tcpServiceConfig: map[string]*runtime.TCPServiceInfo{ + "foo-service": { + TCPService: &dynamic.TCPService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Port: "8085", + Address: "127.0.0.1:8085", + }, + { + Address: "127.0.0.1:8086", + Port: "8086", + }, + }, + }, + }, + }, + }, + tcpRouterConfig: map[string]*runtime.TCPRouterInfo{ + "bar": { + TCPRouter: &dynamic.TCPRouter{ + EntryPoints: []string{"web"}, + Service: "foo-service", + Rule: "HostSNI(`foo.bar`)", + TLS: &dynamic.RouterTCPTLSConfig{}, + Priority: math.MaxInt, + }, + }, + }, + expectedError: 1, + }, { desc: "Router with HostSNI but no TLS", tcpServiceConfig: map[string]*runtime.TCPServiceInfo{ From c84b510f0d1848b7d112b63075a0403140edbf73 Mon Sep 17 00:00:00 2001 From: Manuel Zapf Date: Tue, 2 Apr 2024 17:32:04 +0200 Subject: [PATCH 12/16] Toggle support for Gateway API experimental channel --- docs/content/migration/v2-to-v3.md | 28 ++++++++++ docs/content/providers/kubernetes-gateway.md | 23 +++++++++ .../reference/static-configuration/cli-ref.md | 3 ++ .../reference/static-configuration/env-ref.md | 3 ++ .../reference/static-configuration/file.toml | 1 + .../reference/static-configuration/file.yaml | 1 + integration/fixtures/k8s_gateway.toml | 1 + pkg/provider/kubernetes/gateway/client.go | 22 ++++---- pkg/provider/kubernetes/gateway/kubernetes.go | 51 ++++++++++++++----- .../kubernetes/gateway/kubernetes_test.go | 51 +++++++++++-------- 10 files changed, 140 insertions(+), 44 deletions(-) diff --git a/docs/content/migration/v2-to-v3.md b/docs/content/migration/v2-to-v3.md index f2ec92c1a..6fb88c0e7 100644 --- a/docs/content/migration/v2-to-v3.md +++ b/docs/content/migration/v2-to-v3.md @@ -92,6 +92,34 @@ Docker provider `tls.CAOptional` option has been removed in v3, as TLS client au The `tls.caOptional` option should be removed from the Docker provider static configuration. +### Kubernetes Gateway API + +#### Experimental Channel Resources (TLSRoute and TCPRoute) + +In v3, the Kubernetes Gateway API provider does not enable support for the experimental channel API resources by default. + +##### Remediation + +The `experimentalChannel` option should be used to enable the support for the experimental channel API resources. + +??? example "An example usage of the Kubernetes Gateway API provider with experimental channel support enabled" + + ```yaml tab="File (YAML)" + providers: + kubernetesGateway: + experimentalChannel: true + ``` + + ```toml tab="File (TOML)" + [providers.kubernetesGateway] + experimentalChannel = true + # ... + ``` + + ```bash tab="CLI" + --providers.kubernetesgateway.experimentalchannel=true + ``` + ### Experimental Configuration #### HTTP3 diff --git a/docs/content/providers/kubernetes-gateway.md b/docs/content/providers/kubernetes-gateway.md index 95b5ffd02..fe49dbe75 100644 --- a/docs/content/providers/kubernetes-gateway.md +++ b/docs/content/providers/kubernetes-gateway.md @@ -212,6 +212,29 @@ providers: --providers.kubernetesgateway.namespaces=default,production ``` +### `experimentalChannel` + +_Optional, Default: false_ + +Toggles support for the Experimental Channel resources ([Gateway API release channels documentation](https://gateway-api.sigs.k8s.io/concepts/versioning/#release-channels)). +This option currently enables support for `TCPRoute` and `TLSRoute`. + +```yaml tab="File (YAML)" +providers: + kubernetesGateway: + experimentalChannel: true +``` + +```toml tab="File (TOML)" +[providers.kubernetesGateway] + experimentalChannel = true + # ... +``` + +```bash tab="CLI" +--providers.kubernetesgateway.experimentalchannel=true +``` + ### `labelselector` _Optional, Default: ""_ diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md index 45cd217a5..a632402ad 100644 --- a/docs/content/reference/static-configuration/cli-ref.md +++ b/docs/content/reference/static-configuration/cli-ref.md @@ -729,6 +729,9 @@ Kubernetes certificate authority file path (not needed for in-cluster client). `--providers.kubernetesgateway.endpoint`: Kubernetes server endpoint (required for external cluster client). +`--providers.kubernetesgateway.experimentalchannel`: +Toggles Experimental Channel resources support (TCPRoute, TLSRoute...). (Default: ```false```) + `--providers.kubernetesgateway.labelselector`: Kubernetes label selector to select specific GatewayClasses. diff --git a/docs/content/reference/static-configuration/env-ref.md b/docs/content/reference/static-configuration/env-ref.md index f7ab1bd79..3fe8614e5 100644 --- a/docs/content/reference/static-configuration/env-ref.md +++ b/docs/content/reference/static-configuration/env-ref.md @@ -729,6 +729,9 @@ Kubernetes certificate authority file path (not needed for in-cluster client). `TRAEFIK_PROVIDERS_KUBERNETESGATEWAY_ENDPOINT`: Kubernetes server endpoint (required for external cluster client). +`TRAEFIK_PROVIDERS_KUBERNETESGATEWAY_EXPERIMENTALCHANNEL`: +Toggles Experimental Channel resources support (TCPRoute, TLSRoute...). (Default: ```false```) + `TRAEFIK_PROVIDERS_KUBERNETESGATEWAY_LABELSELECTOR`: Kubernetes label selector to select specific GatewayClasses. diff --git a/docs/content/reference/static-configuration/file.toml b/docs/content/reference/static-configuration/file.toml index cef7d56e9..6bf62c9cf 100644 --- a/docs/content/reference/static-configuration/file.toml +++ b/docs/content/reference/static-configuration/file.toml @@ -146,6 +146,7 @@ namespaces = ["foobar", "foobar"] labelSelector = "foobar" throttleDuration = "42s" + experimentalChannel = true [providers.rest] insecure = true [providers.consulCatalog] diff --git a/docs/content/reference/static-configuration/file.yaml b/docs/content/reference/static-configuration/file.yaml index 84404356d..d54e06b60 100644 --- a/docs/content/reference/static-configuration/file.yaml +++ b/docs/content/reference/static-configuration/file.yaml @@ -163,6 +163,7 @@ providers: - foobar labelSelector: foobar throttleDuration: 42s + experimentalChannel: true rest: insecure: true consulCatalog: diff --git a/integration/fixtures/k8s_gateway.toml b/integration/fixtures/k8s_gateway.toml index 0649ab249..4db5bde6a 100644 --- a/integration/fixtures/k8s_gateway.toml +++ b/integration/fixtures/k8s_gateway.toml @@ -27,3 +27,4 @@ address = ":8443" [providers.kubernetesGateway] + experimentalChannel = true diff --git a/pkg/provider/kubernetes/gateway/client.go b/pkg/provider/kubernetes/gateway/client.go index 9bf57a5f8..05ae47f44 100644 --- a/pkg/provider/kubernetes/gateway/client.go +++ b/pkg/provider/kubernetes/gateway/client.go @@ -75,7 +75,8 @@ type clientWrapper struct { isNamespaceAll bool watchedNamespaces []string - labelSelector string + labelSelector string + experimentalChannel bool } func createClientFromConfig(c *rest.Config) (*clientWrapper, error) { @@ -196,19 +197,22 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (< if err != nil { return nil, err } - _, err = factoryGateway.Gateway().V1alpha2().TCPRoutes().Informer().AddEventHandler(eventHandler) - if err != nil { - return nil, err - } - _, err = factoryGateway.Gateway().V1alpha2().TLSRoutes().Informer().AddEventHandler(eventHandler) - if err != nil { - return nil, err - } _, err = factoryGateway.Gateway().V1beta1().ReferenceGrants().Informer().AddEventHandler(eventHandler) if err != nil { return nil, err } + if c.experimentalChannel { + _, err = factoryGateway.Gateway().V1alpha2().TCPRoutes().Informer().AddEventHandler(eventHandler) + if err != nil { + return nil, err + } + _, err = factoryGateway.Gateway().V1alpha2().TLSRoutes().Informer().AddEventHandler(eventHandler) + if err != nil { + return nil, err + } + } + factoryKube := kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithNamespace(ns)) _, err = factoryKube.Core().V1().Services().Informer().AddEventHandler(eventHandler) if err != nil { diff --git a/pkg/provider/kubernetes/gateway/kubernetes.go b/pkg/provider/kubernetes/gateway/kubernetes.go index 360ae1b8d..01401d750 100644 --- a/pkg/provider/kubernetes/gateway/kubernetes.go +++ b/pkg/provider/kubernetes/gateway/kubernetes.go @@ -51,13 +51,15 @@ const ( // Provider holds configurations of the provider. type Provider struct { - Endpoint string `description:"Kubernetes server endpoint (required for external cluster client)." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"` - Token types.FileOrContent `description:"Kubernetes bearer token (not needed for in-cluster client). It accepts either a token value or a file path to the token." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" loggable:"false"` - CertAuthFilePath string `description:"Kubernetes certificate authority file path (not needed for in-cluster client)." json:"certAuthFilePath,omitempty" toml:"certAuthFilePath,omitempty" yaml:"certAuthFilePath,omitempty"` - Namespaces []string `description:"Kubernetes namespaces." json:"namespaces,omitempty" toml:"namespaces,omitempty" yaml:"namespaces,omitempty" export:"true"` - LabelSelector string `description:"Kubernetes label selector to select specific GatewayClasses." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true"` - ThrottleDuration ptypes.Duration `description:"Kubernetes refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"` - EntryPoints map[string]Entrypoint `json:"-" toml:"-" yaml:"-" label:"-" file:"-"` + Endpoint string `description:"Kubernetes server endpoint (required for external cluster client)." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"` + Token types.FileOrContent `description:"Kubernetes bearer token (not needed for in-cluster client). It accepts either a token value or a file path to the token." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" loggable:"false"` + CertAuthFilePath string `description:"Kubernetes certificate authority file path (not needed for in-cluster client)." json:"certAuthFilePath,omitempty" toml:"certAuthFilePath,omitempty" yaml:"certAuthFilePath,omitempty"` + Namespaces []string `description:"Kubernetes namespaces." json:"namespaces,omitempty" toml:"namespaces,omitempty" yaml:"namespaces,omitempty" export:"true"` + LabelSelector string `description:"Kubernetes label selector to select specific GatewayClasses." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true"` + ThrottleDuration ptypes.Duration `description:"Kubernetes refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"` + ExperimentalChannel bool `description:"Toggles Experimental Channel resources support (TCPRoute, TLSRoute...)." json:"experimentalChannel,omitempty" toml:"experimentalChannel,omitempty" yaml:"experimentalChannel,omitempty" export:"true"` + + EntryPoints map[string]Entrypoint `json:"-" toml:"-" yaml:"-" label:"-" file:"-"` // groupKindFilterFuncs is the list of allowed Group and Kinds for the Filter ExtensionRef objects. groupKindFilterFuncs map[string]map[string]BuildFilterFunc @@ -155,6 +157,7 @@ func (p *Provider) newK8sClient(ctx context.Context) (*clientWrapper, error) { } client.labelSelector = p.LabelSelector + client.experimentalChannel = p.ExperimentalChannel return client, nil } @@ -396,7 +399,7 @@ func (p *Provider) fillGatewayConf(ctx context.Context, client Client, gateway * // AttachedRoutes: 0 TODO Set to number of Routes associated with a Listener regardless of Gateway or Route status } - supportedKinds, conditions := supportedRouteKinds(listener.Protocol) + supportedKinds, conditions := supportedRouteKinds(listener.Protocol, p.ExperimentalChannel) if len(conditions) > 0 { listenerStatuses[i].Conditions = append(listenerStatuses[i].Conditions, conditions...) continue @@ -716,21 +719,41 @@ func (p *Provider) entryPointName(port gatev1.PortNumber, protocol gatev1.Protoc return "", fmt.Errorf("no matching entryPoint for port %d and protocol %q", port, protocol) } -func supportedRouteKinds(protocol gatev1.ProtocolType) ([]gatev1.RouteGroupKind, []metav1.Condition) { +func supportedRouteKinds(protocol gatev1.ProtocolType, experimentalChannel bool) ([]gatev1.RouteGroupKind, []metav1.Condition) { group := gatev1.Group(gatev1.GroupName) switch protocol { case gatev1.TCPProtocolType: - return []gatev1.RouteGroupKind{{Kind: kindTCPRoute, Group: &group}}, nil + if experimentalChannel { + return []gatev1.RouteGroupKind{{Kind: kindTCPRoute, Group: &group}}, nil + } + + return nil, []metav1.Condition{{ + Type: string(gatev1.ListenerConditionConflicted), + Status: metav1.ConditionFalse, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.ListenerReasonInvalidRouteKinds), + Message: fmt.Sprintf("Protocol %q requires the experimental channel support to be enabled, please use the `experimentalChannel` option", protocol), + }} case gatev1.HTTPProtocolType, gatev1.HTTPSProtocolType: return []gatev1.RouteGroupKind{{Kind: kindHTTPRoute, Group: &group}}, nil case gatev1.TLSProtocolType: - return []gatev1.RouteGroupKind{ - {Kind: kindTCPRoute, Group: &group}, - {Kind: kindTLSRoute, Group: &group}, - }, nil + if experimentalChannel { + return []gatev1.RouteGroupKind{ + {Kind: kindTCPRoute, Group: &group}, + {Kind: kindTLSRoute, Group: &group}, + }, nil + } + + return nil, []metav1.Condition{{ + Type: string(gatev1.ListenerConditionConflicted), + Status: metav1.ConditionFalse, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.ListenerReasonInvalidRouteKinds), + Message: fmt.Sprintf("Protocol %q requires the experimental channel support to be enabled, please use the `experimentalChannel` option", protocol), + }} } return nil, []metav1.Condition{{ diff --git a/pkg/provider/kubernetes/gateway/kubernetes_test.go b/pkg/provider/kubernetes/gateway/kubernetes_test.go index 8146523f9..85f055b39 100644 --- a/pkg/provider/kubernetes/gateway/kubernetes_test.go +++ b/pkg/provider/kubernetes/gateway/kubernetes_test.go @@ -24,12 +24,12 @@ var _ provider.Provider = (*Provider)(nil) func TestLoadHTTPRoutes(t *testing.T) { testCases := []struct { - desc string - ingressClass string - paths []string - namespaces []string - expected *dynamic.Configuration - entryPoints map[string]Entrypoint + desc string + ingressClass string + paths []string + expected *dynamic.Configuration + entryPoints map[string]Entrypoint + experimentalChannel bool }{ { desc: "Empty", @@ -476,6 +476,7 @@ func TestLoadHTTPRoutes(t *testing.T) { entryPoints: map[string]Entrypoint{"TCP": { Address: ":8080", }}, + experimentalChannel: true, expected: &dynamic.Configuration{ UDP: &dynamic.UDPConfiguration{ Routers: map[string]*dynamic.UDPRouter{}, @@ -1664,7 +1665,7 @@ func TestLoadHTTPRoutes(t *testing.T) { return } - p := Provider{EntryPoints: test.entryPoints} + p := Provider{EntryPoints: test.entryPoints, ExperimentalChannel: test.experimentalChannel} conf := p.loadConfigurationFromGateway(context.Background(), newClientMock(test.paths...)) assert.Equal(t, test.expected, conf) @@ -2905,7 +2906,7 @@ func TestLoadTCPRoutes(t *testing.T) { return } - p := Provider{EntryPoints: test.entryPoints} + p := Provider{EntryPoints: test.entryPoints, ExperimentalChannel: true} conf := p.loadConfigurationFromGateway(context.Background(), newClientMock(test.paths...)) assert.Equal(t, test.expected, conf) }) @@ -4034,7 +4035,7 @@ func TestLoadTLSRoutes(t *testing.T) { return } - p := Provider{EntryPoints: test.entryPoints} + p := Provider{EntryPoints: test.entryPoints, ExperimentalChannel: true} conf := p.loadConfigurationFromGateway(context.Background(), newClientMock(test.paths...)) assert.Equal(t, test.expected, conf) }) @@ -4043,11 +4044,12 @@ func TestLoadTLSRoutes(t *testing.T) { func TestLoadMixedRoutes(t *testing.T) { testCases := []struct { - desc string - ingressClass string - paths []string - expected *dynamic.Configuration - entryPoints map[string]Entrypoint + desc string + ingressClass string + paths []string + expected *dynamic.Configuration + entryPoints map[string]Entrypoint + experimentalChannel bool }{ { desc: "Empty", @@ -4159,6 +4161,7 @@ func TestLoadMixedRoutes(t *testing.T) { "tls-1": {Address: ":10000"}, "tls-2": {Address: ":11000"}, }, + experimentalChannel: true, expected: &dynamic.Configuration{ UDP: &dynamic.UDPConfiguration{ Routers: map[string]*dynamic.UDPRouter{}, @@ -4343,6 +4346,7 @@ func TestLoadMixedRoutes(t *testing.T) { "tls-1": {Address: ":10000"}, "tls-2": {Address: ":11000"}, }, + experimentalChannel: true, expected: &dynamic.Configuration{ UDP: &dynamic.UDPConfiguration{ Routers: map[string]*dynamic.UDPRouter{}, @@ -4499,6 +4503,7 @@ func TestLoadMixedRoutes(t *testing.T) { "tls-1": {Address: ":10000"}, "tls-2": {Address: ":11000"}, }, + experimentalChannel: true, expected: &dynamic.Configuration{ UDP: &dynamic.UDPConfiguration{ Routers: map[string]*dynamic.UDPRouter{}, @@ -4749,6 +4754,7 @@ func TestLoadMixedRoutes(t *testing.T) { "tls-1": {Address: ":10000"}, "tls-2": {Address: ":11000"}, }, + experimentalChannel: true, expected: &dynamic.Configuration{ UDP: &dynamic.UDPConfiguration{ Routers: map[string]*dynamic.UDPRouter{}, @@ -4904,6 +4910,7 @@ func TestLoadMixedRoutes(t *testing.T) { "tcp": {Address: ":9000"}, "tls": {Address: ":10000"}, }, + experimentalChannel: true, expected: &dynamic.Configuration{ UDP: &dynamic.UDPConfiguration{ Routers: map[string]*dynamic.UDPRouter{}, @@ -5042,7 +5049,7 @@ func TestLoadMixedRoutes(t *testing.T) { return } - p := Provider{EntryPoints: test.entryPoints} + p := Provider{EntryPoints: test.entryPoints, ExperimentalChannel: test.experimentalChannel} conf := p.loadConfigurationFromGateway(context.Background(), newClientMock(test.paths...)) assert.Equal(t, test.expected, conf) }) @@ -5051,11 +5058,12 @@ func TestLoadMixedRoutes(t *testing.T) { func TestLoadRoutesWithReferenceGrants(t *testing.T) { testCases := []struct { - desc string - ingressClass string - paths []string - expected *dynamic.Configuration - entryPoints map[string]Entrypoint + desc string + ingressClass string + paths []string + expected *dynamic.Configuration + entryPoints map[string]Entrypoint + experimentalChannel bool }{ { desc: "Empty", @@ -5163,6 +5171,7 @@ func TestLoadRoutesWithReferenceGrants(t *testing.T) { entryPoints: map[string]Entrypoint{ "tls": {Address: ":9000"}, }, + experimentalChannel: true, expected: &dynamic.Configuration{ UDP: &dynamic.UDPConfiguration{ Routers: map[string]*dynamic.UDPRouter{}, @@ -5232,7 +5241,7 @@ func TestLoadRoutesWithReferenceGrants(t *testing.T) { return } - p := Provider{EntryPoints: test.entryPoints} + p := Provider{EntryPoints: test.entryPoints, ExperimentalChannel: test.experimentalChannel} conf := p.loadConfigurationFromGateway(context.Background(), newClientMock(test.paths...)) assert.Equal(t, test.expected, conf) }) From bbd5846c6af578746c5719116a5699e62299b30c Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Wed, 3 Apr 2024 18:46:03 +0200 Subject: [PATCH 13/16] Update Yaegi to v0.16.1 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d8441118e..c9cd7a9ab 100644 --- a/go.mod +++ b/go.mod @@ -58,7 +58,7 @@ require ( github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154 github.com/testcontainers/testcontainers-go v0.27.0 github.com/traefik/paerser v0.2.0 - github.com/traefik/yaegi v0.15.1 + github.com/traefik/yaegi v0.16.1 github.com/uber/jaeger-client-go v2.30.0+incompatible github.com/uber/jaeger-lib v2.2.0+incompatible github.com/unrolled/render v1.0.2 diff --git a/go.sum b/go.sum index b3a1be0b6..cb7a4cfd6 100644 --- a/go.sum +++ b/go.sum @@ -1244,8 +1244,8 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/traefik/paerser v0.2.0 h1:zqCLGSXoNlcBd+mzqSCLjon/I6phqIjeJL2xFB2ysgQ= github.com/traefik/paerser v0.2.0/go.mod h1:afzaVcgF8A+MpTnPG4wBr4whjanCSYA6vK5RwaYVtRc= -github.com/traefik/yaegi v0.15.1 h1:YA5SbaL6HZA0Exh9T/oArRHqGN2HQ+zgmCY7dkoTXu4= -github.com/traefik/yaegi v0.15.1/go.mod h1:AVRxhaI2G+nUsaM1zyktzwXn69G3t/AuTDrCiTds9p0= +github.com/traefik/yaegi v0.16.1 h1:f1De3DVJqIDKmnasUF6MwmWv1dSEEat0wcpXhD2On3E= +github.com/traefik/yaegi v0.16.1/go.mod h1:4eVhbPb3LnD2VigQjhYbEJ69vDRFdT2HQNrXx8eEwUY= github.com/transip/gotransip/v6 v6.23.0 h1:PsTdjortrEZ8IFFifEryzjVjOy9SgK4ahlnhKBBIQgA= github.com/transip/gotransip/v6 v6.23.0/go.mod h1:nzv9eN2tdsUrm5nG5ZX6AugYIU4qgsMwIn2c0EZLk8c= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= From 945ff9b0f90b220417035184f3431ff8af7aaf12 Mon Sep 17 00:00:00 2001 From: Michel Loiseleur <97035654+mloiseleur@users.noreply.github.com> Date: Wed, 3 Apr 2024 19:08:03 +0200 Subject: [PATCH 14/16] chore(ci): fix and update codeql --- .github/workflows/codeql.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index d3ef51b41..f921d7789 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -30,9 +30,15 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + - name: setup go + uses: actions/setup-go@v5 + if: ${{ matrix.language == 'go' }} + with: + go-version-file: 'go.mod' + # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -46,7 +52,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -59,6 +65,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 with: category: "/language:${{matrix.language}}" From 2c6418e17ac6580b4726e08c70132b1c540f92c1 Mon Sep 17 00:00:00 2001 From: Michel Loiseleur <97035654+mloiseleur@users.noreply.github.com> Date: Thu, 4 Apr 2024 10:14:06 +0200 Subject: [PATCH 15/16] docs: fix typo and improve explanation on internal resources --- docs/content/migration/v2-to-v3.md | 4 ++-- docs/content/observability/access-logs.md | 4 ++-- docs/content/observability/metrics/overview.md | 2 +- docs/content/observability/tracing/overview.md | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/content/migration/v2-to-v3.md b/docs/content/migration/v2-to-v3.md index 6fb88c0e7..61a60d990 100644 --- a/docs/content/migration/v2-to-v3.md +++ b/docs/content/migration/v2-to-v3.md @@ -724,7 +724,7 @@ Here are two possible transition strategies: Please check the [OpenTelemetry Tracing provider documention](../observability/tracing/opentelemetry.md) for more information. -#### Internal Resources Observability (AccessLogs, Metrics and Tracing) +#### Internal Resources Observability In v3, observability for internal routers or services (e.g.: `ping@internal`) is disabled by default. To enable it one should use the new `addInternals` option for AccessLogs, Metrics or Tracing. @@ -732,4 +732,4 @@ Please take a look at the observability documentation for more information: - [AccessLogs](../observability/access-logs.md#addinternals) - [Metrics](../observability/metrics/overview.md#addinternals) -- [AccessLogs](../observability/tracing/overview.md#addinternals) +- [Tracing](../observability/tracing/overview.md#addinternals) diff --git a/docs/content/observability/access-logs.md b/docs/content/observability/access-logs.md index beb114db1..2105cc030 100644 --- a/docs/content/observability/access-logs.md +++ b/docs/content/observability/access-logs.md @@ -30,7 +30,7 @@ accessLog: {} _Optional, Default="false"_ -Enables accessLogs for internal resources. +Enables accessLogs for internal resources (e.g.: `ping@internal`). ```yaml tab="File (YAML)" accesslog: @@ -187,7 +187,7 @@ accessLog: [accessLog.fields] defaultMode = "keep" - + [accessLog.fields.names] "ClientUsername" = "drop" diff --git a/docs/content/observability/metrics/overview.md b/docs/content/observability/metrics/overview.md index 082d4aed0..a49c5053c 100644 --- a/docs/content/observability/metrics/overview.md +++ b/docs/content/observability/metrics/overview.md @@ -21,7 +21,7 @@ and [Kubernetes](https://grafana.com/grafana/dashboards/17347) deployments. _Optional, Default="false"_ -Enables metrics for internal resources. +Enables metrics for internal resources (e.g.: `ping@internals`). ```yaml tab="File (YAML)" metrics: diff --git a/docs/content/observability/tracing/overview.md b/docs/content/observability/tracing/overview.md index 72dd20cd6..e81f63edf 100644 --- a/docs/content/observability/tracing/overview.md +++ b/docs/content/observability/tracing/overview.md @@ -36,7 +36,7 @@ tracing: {} _Optional, Default="false"_ -Enables tracing for internal resources. +Enables tracing for internal resources (e.g.: `ping@internal`). ```yaml tab="File (YAML)" tracing: @@ -159,4 +159,4 @@ tracing: ```bash tab="CLI" --tracing.capturedResponseHeaders[0]=X-CustomHeader -``` \ No newline at end of file +``` From ac1753a614c0105b3f73efa4a17cdf45aca37347 Mon Sep 17 00:00:00 2001 From: chrispruitt Date: Thu, 4 Apr 2024 05:54:04 -0400 Subject: [PATCH 16/16] Nomad provider to allow empty services --- docs/content/providers/nomad.md | 24 ++ .../reference/static-configuration/cli-ref.md | 3 + .../reference/static-configuration/env-ref.md | 3 + .../reference/static-configuration/file.toml | 1 + .../reference/static-configuration/file.yaml | 1 + pkg/provider/nomad/config.go | 21 +- pkg/provider/nomad/config_test.go | 337 +++++++++++++++ .../job_job1_WithGroupService_Scaling1.json | 220 ++++++++++ .../job_job2_WithGroupService_Scaling0.json | 220 ++++++++++ ...job3_WithGroupService_ScalingDisabled.json | 206 ++++++++++ ...hGroupService_ScalingDisabled_Stopped.json | 206 ++++++++++ ...ob_job5_WithGroupTaskService_Scaling1.json | 299 ++++++++++++++ ...ob_job6_WithGroupTaskService_Scaling0.json | 299 ++++++++++++++ pkg/provider/nomad/fixtures/job_job7_TCP.json | 222 ++++++++++ pkg/provider/nomad/fixtures/job_job8_UDP.json | 221 ++++++++++ .../job_job9_ScalingEnabled_Stopped.json | 220 ++++++++++ pkg/provider/nomad/fixtures/jobs_job1.json | 56 +++ pkg/provider/nomad/fixtures/jobs_job2.json | 47 +++ pkg/provider/nomad/fixtures/jobs_job3.json | 47 +++ pkg/provider/nomad/fixtures/jobs_job4.json | 47 +++ pkg/provider/nomad/fixtures/jobs_job5.json | 56 +++ pkg/provider/nomad/fixtures/jobs_job6.json | 47 +++ pkg/provider/nomad/fixtures/jobs_job7.json | 47 +++ pkg/provider/nomad/fixtures/jobs_job8.json | 47 +++ pkg/provider/nomad/fixtures/jobs_job9.json | 47 +++ .../nomad/fixtures/service_hello.json | 20 + pkg/provider/nomad/fixtures/service_job1.json | 18 + pkg/provider/nomad/fixtures/service_job2.json | 1 + pkg/provider/nomad/fixtures/service_job3.json | 18 + pkg/provider/nomad/fixtures/service_job4.json | 1 + .../nomad/fixtures/service_job5task1.json | 18 + .../nomad/fixtures/service_job5task2.json | 18 + .../nomad/fixtures/service_job6task1.json | 1 + .../nomad/fixtures/service_job6task2.json | 1 + pkg/provider/nomad/fixtures/service_job7.json | 20 + pkg/provider/nomad/fixtures/service_job8.json | 19 + pkg/provider/nomad/fixtures/service_job9.json | 1 + .../nomad/fixtures/service_redis.json | 18 + pkg/provider/nomad/fixtures/services.json | 21 + pkg/provider/nomad/nomad.go | 124 +++++- pkg/provider/nomad/nomad_test.go | 384 ++++++++++++++---- 41 files changed, 3540 insertions(+), 87 deletions(-) create mode 100644 pkg/provider/nomad/fixtures/job_job1_WithGroupService_Scaling1.json create mode 100644 pkg/provider/nomad/fixtures/job_job2_WithGroupService_Scaling0.json create mode 100644 pkg/provider/nomad/fixtures/job_job3_WithGroupService_ScalingDisabled.json create mode 100644 pkg/provider/nomad/fixtures/job_job4_WithGroupService_ScalingDisabled_Stopped.json create mode 100644 pkg/provider/nomad/fixtures/job_job5_WithGroupTaskService_Scaling1.json create mode 100644 pkg/provider/nomad/fixtures/job_job6_WithGroupTaskService_Scaling0.json create mode 100644 pkg/provider/nomad/fixtures/job_job7_TCP.json create mode 100644 pkg/provider/nomad/fixtures/job_job8_UDP.json create mode 100644 pkg/provider/nomad/fixtures/job_job9_ScalingEnabled_Stopped.json create mode 100644 pkg/provider/nomad/fixtures/jobs_job1.json create mode 100644 pkg/provider/nomad/fixtures/jobs_job2.json create mode 100644 pkg/provider/nomad/fixtures/jobs_job3.json create mode 100644 pkg/provider/nomad/fixtures/jobs_job4.json create mode 100644 pkg/provider/nomad/fixtures/jobs_job5.json create mode 100644 pkg/provider/nomad/fixtures/jobs_job6.json create mode 100644 pkg/provider/nomad/fixtures/jobs_job7.json create mode 100644 pkg/provider/nomad/fixtures/jobs_job8.json create mode 100644 pkg/provider/nomad/fixtures/jobs_job9.json create mode 100644 pkg/provider/nomad/fixtures/service_hello.json create mode 100644 pkg/provider/nomad/fixtures/service_job1.json create mode 100644 pkg/provider/nomad/fixtures/service_job2.json create mode 100644 pkg/provider/nomad/fixtures/service_job3.json create mode 100644 pkg/provider/nomad/fixtures/service_job4.json create mode 100644 pkg/provider/nomad/fixtures/service_job5task1.json create mode 100644 pkg/provider/nomad/fixtures/service_job5task2.json create mode 100644 pkg/provider/nomad/fixtures/service_job6task1.json create mode 100644 pkg/provider/nomad/fixtures/service_job6task2.json create mode 100644 pkg/provider/nomad/fixtures/service_job7.json create mode 100644 pkg/provider/nomad/fixtures/service_job8.json create mode 100644 pkg/provider/nomad/fixtures/service_job9.json create mode 100644 pkg/provider/nomad/fixtures/service_redis.json create mode 100644 pkg/provider/nomad/fixtures/services.json diff --git a/docs/content/providers/nomad.md b/docs/content/providers/nomad.md index f1d0d1fe1..85b3ac033 100644 --- a/docs/content/providers/nomad.md +++ b/docs/content/providers/nomad.md @@ -511,3 +511,27 @@ providers: --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 +``` diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md index a632402ad..6194dfbfe 100644 --- a/docs/content/reference/static-configuration/cli-ref.md +++ b/docs/content/reference/static-configuration/cli-ref.md @@ -789,6 +789,9 @@ Kubernetes bearer token (not needed for in-cluster client). It accepts either a `--providers.nomad`: Enable Nomad backend with default settings. (Default: ```false```) +`--providers.nomad.allowemptyservices`: +Allow the creation of services without endpoints. (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. diff --git a/docs/content/reference/static-configuration/env-ref.md b/docs/content/reference/static-configuration/env-ref.md index 3fe8614e5..7fcde07fc 100644 --- a/docs/content/reference/static-configuration/env-ref.md +++ b/docs/content/reference/static-configuration/env-ref.md @@ -789,6 +789,9 @@ Kubernetes bearer token (not needed for in-cluster client). It accepts either a `TRAEFIK_PROVIDERS_NOMAD`: 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`: Constraints is an expression that Traefik matches against the Nomad service's tags to determine whether to create route(s) for that service. diff --git a/docs/content/reference/static-configuration/file.toml b/docs/content/reference/static-configuration/file.toml index 6bf62c9cf..e5a6e379e 100644 --- a/docs/content/reference/static-configuration/file.toml +++ b/docs/content/reference/static-configuration/file.toml @@ -185,6 +185,7 @@ stale = true exposedByDefault = true refreshInterval = "42s" + allowEmptyServices = true namespaces = ["foobar", "foobar"] [providers.nomad.endpoint] address = "foobar" diff --git a/docs/content/reference/static-configuration/file.yaml b/docs/content/reference/static-configuration/file.yaml index d54e06b60..362418fbc 100644 --- a/docs/content/reference/static-configuration/file.yaml +++ b/docs/content/reference/static-configuration/file.yaml @@ -216,6 +216,7 @@ providers: stale: true exposedByDefault: true refreshInterval: 42s + allowEmptyServices: true namespaces: - foobar - foobar diff --git a/pkg/provider/nomad/config.go b/pkg/provider/nomad/config.go index b6caa8cf1..83022f31d 100644 --- a/pkg/provider/nomad/config.go +++ b/pkg/provider/nomad/config.go @@ -97,8 +97,11 @@ func (p *Provider) buildTCPConfig(i item, configuration *dynamic.TCPConfiguratio } for _, service := range configuration.Services { - if err := p.addServerTCP(i, service.LoadBalancer); err != nil { - return err + // Leave load balancer empty when no address and allowEmptyServices = true + 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 { - if err := p.addServerUDP(i, service.LoadBalancer); err != nil { - return err + // Leave load balancer empty when no address and allowEmptyServices = true + 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 { - if err := p.addServer(i, service.LoadBalancer); err != nil { - return err + // Leave load balancer empty when no address and allowEmptyServices = true + if !(i.Address == "" && p.AllowEmptyServices) { + if err := p.addServer(i, service.LoadBalancer); err != nil { + return err + } } } diff --git a/pkg/provider/nomad/config_test.go b/pkg/provider/nomad/config_test.go index e1ec061e5..2f036e929 100644 --- a/pkg/provider/nomad/config_test.go +++ b/pkg/provider/nomad/config_test.go @@ -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", 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) { testCases := []struct { name string diff --git a/pkg/provider/nomad/fixtures/job_job1_WithGroupService_Scaling1.json b/pkg/provider/nomad/fixtures/job_job1_WithGroupService_Scaling1.json new file mode 100644 index 000000000..9044736c6 --- /dev/null +++ b/pkg/provider/nomad/fixtures/job_job1_WithGroupService_Scaling1.json @@ -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 +} \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/job_job2_WithGroupService_Scaling0.json b/pkg/provider/nomad/fixtures/job_job2_WithGroupService_Scaling0.json new file mode 100644 index 000000000..0cf8656f7 --- /dev/null +++ b/pkg/provider/nomad/fixtures/job_job2_WithGroupService_Scaling0.json @@ -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 +} \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/job_job3_WithGroupService_ScalingDisabled.json b/pkg/provider/nomad/fixtures/job_job3_WithGroupService_ScalingDisabled.json new file mode 100644 index 000000000..a0efc5e16 --- /dev/null +++ b/pkg/provider/nomad/fixtures/job_job3_WithGroupService_ScalingDisabled.json @@ -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 +} \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/job_job4_WithGroupService_ScalingDisabled_Stopped.json b/pkg/provider/nomad/fixtures/job_job4_WithGroupService_ScalingDisabled_Stopped.json new file mode 100644 index 000000000..55d312dcd --- /dev/null +++ b/pkg/provider/nomad/fixtures/job_job4_WithGroupService_ScalingDisabled_Stopped.json @@ -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 +} \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/job_job5_WithGroupTaskService_Scaling1.json b/pkg/provider/nomad/fixtures/job_job5_WithGroupTaskService_Scaling1.json new file mode 100644 index 000000000..f0171814d --- /dev/null +++ b/pkg/provider/nomad/fixtures/job_job5_WithGroupTaskService_Scaling1.json @@ -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 +} \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/job_job6_WithGroupTaskService_Scaling0.json b/pkg/provider/nomad/fixtures/job_job6_WithGroupTaskService_Scaling0.json new file mode 100644 index 000000000..a4528e4dd --- /dev/null +++ b/pkg/provider/nomad/fixtures/job_job6_WithGroupTaskService_Scaling0.json @@ -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 +} \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/job_job7_TCP.json b/pkg/provider/nomad/fixtures/job_job7_TCP.json new file mode 100644 index 000000000..3bf39a776 --- /dev/null +++ b/pkg/provider/nomad/fixtures/job_job7_TCP.json @@ -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 +} \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/job_job8_UDP.json b/pkg/provider/nomad/fixtures/job_job8_UDP.json new file mode 100644 index 000000000..eda3df6f0 --- /dev/null +++ b/pkg/provider/nomad/fixtures/job_job8_UDP.json @@ -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 +} \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/job_job9_ScalingEnabled_Stopped.json b/pkg/provider/nomad/fixtures/job_job9_ScalingEnabled_Stopped.json new file mode 100644 index 000000000..596072a67 --- /dev/null +++ b/pkg/provider/nomad/fixtures/job_job9_ScalingEnabled_Stopped.json @@ -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 +} \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/jobs_job1.json b/pkg/provider/nomad/fixtures/jobs_job1.json new file mode 100644 index 000000000..66016a6a9 --- /dev/null +++ b/pkg/provider/nomad/fixtures/jobs_job1.json @@ -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 + } +] \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/jobs_job2.json b/pkg/provider/nomad/fixtures/jobs_job2.json new file mode 100644 index 000000000..ee817aa0a --- /dev/null +++ b/pkg/provider/nomad/fixtures/jobs_job2.json @@ -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 + } +] \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/jobs_job3.json b/pkg/provider/nomad/fixtures/jobs_job3.json new file mode 100644 index 000000000..bf69bc069 --- /dev/null +++ b/pkg/provider/nomad/fixtures/jobs_job3.json @@ -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 + } +] \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/jobs_job4.json b/pkg/provider/nomad/fixtures/jobs_job4.json new file mode 100644 index 000000000..dfd0f3a11 --- /dev/null +++ b/pkg/provider/nomad/fixtures/jobs_job4.json @@ -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 + } +] \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/jobs_job5.json b/pkg/provider/nomad/fixtures/jobs_job5.json new file mode 100644 index 000000000..bf846ed21 --- /dev/null +++ b/pkg/provider/nomad/fixtures/jobs_job5.json @@ -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 + } +] \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/jobs_job6.json b/pkg/provider/nomad/fixtures/jobs_job6.json new file mode 100644 index 000000000..e4c34e238 --- /dev/null +++ b/pkg/provider/nomad/fixtures/jobs_job6.json @@ -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 + } +] \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/jobs_job7.json b/pkg/provider/nomad/fixtures/jobs_job7.json new file mode 100644 index 000000000..f3de421e2 --- /dev/null +++ b/pkg/provider/nomad/fixtures/jobs_job7.json @@ -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 + } +] \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/jobs_job8.json b/pkg/provider/nomad/fixtures/jobs_job8.json new file mode 100644 index 000000000..e77e2c718 --- /dev/null +++ b/pkg/provider/nomad/fixtures/jobs_job8.json @@ -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 + } +] \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/jobs_job9.json b/pkg/provider/nomad/fixtures/jobs_job9.json new file mode 100644 index 000000000..2f45e4ae7 --- /dev/null +++ b/pkg/provider/nomad/fixtures/jobs_job9.json @@ -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 + } +] \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/service_hello.json b/pkg/provider/nomad/fixtures/service_hello.json new file mode 100644 index 000000000..e4f675b16 --- /dev/null +++ b/pkg/provider/nomad/fixtures/service_hello.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/service_job1.json b/pkg/provider/nomad/fixtures/service_job1.json new file mode 100644 index 000000000..18a6c2ba0 --- /dev/null +++ b/pkg/provider/nomad/fixtures/service_job1.json @@ -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 + } +] \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/service_job2.json b/pkg/provider/nomad/fixtures/service_job2.json new file mode 100644 index 000000000..0637a088a --- /dev/null +++ b/pkg/provider/nomad/fixtures/service_job2.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/service_job3.json b/pkg/provider/nomad/fixtures/service_job3.json new file mode 100644 index 000000000..ece0f4235 --- /dev/null +++ b/pkg/provider/nomad/fixtures/service_job3.json @@ -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 + } +] \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/service_job4.json b/pkg/provider/nomad/fixtures/service_job4.json new file mode 100644 index 000000000..0637a088a --- /dev/null +++ b/pkg/provider/nomad/fixtures/service_job4.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/service_job5task1.json b/pkg/provider/nomad/fixtures/service_job5task1.json new file mode 100644 index 000000000..a438e607f --- /dev/null +++ b/pkg/provider/nomad/fixtures/service_job5task1.json @@ -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 + } +] \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/service_job5task2.json b/pkg/provider/nomad/fixtures/service_job5task2.json new file mode 100644 index 000000000..a9d32113f --- /dev/null +++ b/pkg/provider/nomad/fixtures/service_job5task2.json @@ -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 + } +] diff --git a/pkg/provider/nomad/fixtures/service_job6task1.json b/pkg/provider/nomad/fixtures/service_job6task1.json new file mode 100644 index 000000000..0637a088a --- /dev/null +++ b/pkg/provider/nomad/fixtures/service_job6task1.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/service_job6task2.json b/pkg/provider/nomad/fixtures/service_job6task2.json new file mode 100644 index 000000000..0637a088a --- /dev/null +++ b/pkg/provider/nomad/fixtures/service_job6task2.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/service_job7.json b/pkg/provider/nomad/fixtures/service_job7.json new file mode 100644 index 000000000..35ab344cf --- /dev/null +++ b/pkg/provider/nomad/fixtures/service_job7.json @@ -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 + } +] \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/service_job8.json b/pkg/provider/nomad/fixtures/service_job8.json new file mode 100644 index 000000000..d670c568a --- /dev/null +++ b/pkg/provider/nomad/fixtures/service_job8.json @@ -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 + } +] \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/service_job9.json b/pkg/provider/nomad/fixtures/service_job9.json new file mode 100644 index 000000000..0637a088a --- /dev/null +++ b/pkg/provider/nomad/fixtures/service_job9.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/service_redis.json b/pkg/provider/nomad/fixtures/service_redis.json new file mode 100644 index 000000000..b1f4e752e --- /dev/null +++ b/pkg/provider/nomad/fixtures/service_redis.json @@ -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" + ] + } +] \ No newline at end of file diff --git a/pkg/provider/nomad/fixtures/services.json b/pkg/provider/nomad/fixtures/services.json new file mode 100644 index 000000000..dfe303183 --- /dev/null +++ b/pkg/provider/nomad/fixtures/services.json @@ -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" + ] + } + ] + } +] \ No newline at end of file diff --git a/pkg/provider/nomad/nomad.go b/pkg/provider/nomad/nomad.go index b836707eb..5a595dca6 100644 --- a/pkg/provider/nomad/nomad.go +++ b/pkg/provider/nomad/nomad.go @@ -85,13 +85,14 @@ func (p *ProviderBuilder) BuildProviders() []*Provider { // Configuration represents the Nomad provider configuration. type Configuration 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"` - 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"` + 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"` + 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"` + 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. @@ -116,6 +117,7 @@ func (c *Configuration) SetDefaults() { c.ExposedByDefault = true c.RefreshInterval = ptypes.Duration(15 * time.Second) c.DefaultRule = defaultTemplateRule + c.AllowEmptyServices = false } 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 { - items, err := p.getNomadServiceData(ctx) - if err != nil { - return err + var items []item + var err error + 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{ ProviderName: p.name, Configuration: p.buildConfig(ctx, items), @@ -291,6 +303,98 @@ func (p *Provider) getNomadServiceData(ctx context.Context) ([]item, error) { 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. ".enable"). func (p *Provider) getExtraConf(tags []string) configuration { labels := tagsToLabels(tags, p.Prefix) diff --git a/pkg/provider/nomad/nomad_test.go b/pkg/provider/nomad/nomad_test.go index ffbb3f89d..2794fab0c 100644 --- a/pkg/provider/nomad/nomad_test.go +++ b/pkg/provider/nomad/nomad_test.go @@ -2,8 +2,11 @@ package nomad import ( "context" + "fmt" "net/http" "net/http/httptest" + "os" + "path/filepath" "strings" "testing" @@ -12,6 +15,17 @@ import ( "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) { cases := []struct { 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) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch { case strings.HasSuffix(r.RequestURI, "/v1/services"): - _, _ = w.Write([]byte(services)) + _, _ = w.Write(responses["services"]) 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"): - _, _ = w.Write([]byte(hello)) + _, _ = w.Write(responses["service_hello"]) } })) t.Cleanup(ts.Close) @@ -159,71 +469,3 @@ func Test_getNomadServiceData(t *testing.T) { 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" - ] - } -] -`