Merge branch 'master' of github.com:traefik/traefik
All checks were successful
Build & Push / build-and-push (push) Successful in 12m7s
All checks were successful
Build & Push / build-and-push (push) Successful in 12m7s
Signed-off-by: baalajimaestro <baalajimaestro@ptr.moe>
This commit is contained in:
commit
ff55677acd
40 changed files with 2066 additions and 1647 deletions
|
@ -25,7 +25,7 @@ global_job_config:
|
||||||
- export "PATH=${GOPATH}/bin:${PATH}"
|
- export "PATH=${GOPATH}/bin:${PATH}"
|
||||||
- mkdir -vp "${SEMAPHORE_GIT_DIR}" "${GOPATH}/bin"
|
- mkdir -vp "${SEMAPHORE_GIT_DIR}" "${GOPATH}/bin"
|
||||||
- export GOPROXY=https://proxy.golang.org,direct
|
- 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.60.3
|
- curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "${GOPATH}/bin" v1.61.0
|
||||||
- curl -sSfL https://gist.githubusercontent.com/traefiker/6d7ac019c11d011e4f131bb2cca8900e/raw/goreleaser.sh | bash -s -- -b "${GOPATH}/bin"
|
- curl -sSfL https://gist.githubusercontent.com/traefiker/6d7ac019c11d011e4f131bb2cca8900e/raw/goreleaser.sh | bash -s -- -b "${GOPATH}/bin"
|
||||||
- checkout
|
- checkout
|
||||||
- cache restore traefik-$(checksum go.sum)
|
- cache restore traefik-$(checksum go.sum)
|
||||||
|
|
43
CHANGELOG.md
43
CHANGELOG.md
|
@ -1,3 +1,44 @@
|
||||||
|
## [v3.2.0-rc2](https://github.com/traefik/traefik/tree/v3.2.0-rc2) (2024-10-09)
|
||||||
|
[All Commits](https://github.com/traefik/traefik/compare/v3.2.0-rc1...v3.2.0-rc2)
|
||||||
|
|
||||||
|
**Enhancements:**
|
||||||
|
- **[k8s,k8s/gatewayapi]** Bump sigs.k8s.io/gateway-api to v1.2.0 ([#11167](https://github.com/traefik/traefik/pull/11167) by [rtribotte](https://github.com/rtribotte))
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[k8s,k8s/gatewayapi]** Support http and https appProtocol for Kubernetes Service ([#11176](https://github.com/traefik/traefik/pull/11176) by [WillDaSilva](https://github.com/WillDaSilva))
|
||||||
|
- **[k8s,k8s/gatewayapi]** Avoid updating Accepted status for routes matching no Gateways ([#11170](https://github.com/traefik/traefik/pull/11170) by [rtribotte](https://github.com/rtribotte))
|
||||||
|
- **[k8s,k8s/gatewayapi]** Do not update gateway status when not selected by a gateway class ([#11169](https://github.com/traefik/traefik/pull/11169) by [kevinpollet](https://github.com/kevinpollet))
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- Detail CRD update with v3.2 in the migration guide ([#11164](https://github.com/traefik/traefik/pull/11164) by [mloiseleur](https://github.com/mloiseleur))
|
||||||
|
|
||||||
|
**Misc:**
|
||||||
|
- Merge branch v3.1 into v3.2 ([#11181](https://github.com/traefik/traefik/pull/11181) by [kevinpollet](https://github.com/kevinpollet))
|
||||||
|
|
||||||
|
## [v3.1.6](https://github.com/traefik/traefik/tree/v3.1.6) (2024-10-09)
|
||||||
|
[All Commits](https://github.com/traefik/traefik/compare/v3.1.5...v3.1.6)
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[middleware]** Reuse compression writers ([#11168](https://github.com/traefik/traefik/pull/11168) by [michelheusschen](https://github.com/michelheusschen))
|
||||||
|
- **[middleware]** Use correct default weight in Accept-Encoding ([#11084](https://github.com/traefik/traefik/pull/11084) by [michelheusschen](https://github.com/michelheusschen))
|
||||||
|
- **[plugins]** Close wasm middleware to prevent memory leak ([#11151](https://github.com/traefik/traefik/pull/11151) by [ttys3](https://github.com/ttys3))
|
||||||
|
|
||||||
|
**Misc:**
|
||||||
|
- Merge branch v2.11 into v3.1 ([#11179](https://github.com/traefik/traefik/pull/11179) by [kevinpollet](https://github.com/kevinpollet))
|
||||||
|
- Merge branch v2.11 into v3.1 ([#11174](https://github.com/traefik/traefik/pull/11174) by [mmatur](https://github.com/mmatur))
|
||||||
|
|
||||||
|
## [v2.11.12](https://github.com/traefik/traefik/tree/v2.11.12) (2024-10-09)
|
||||||
|
[All Commits](https://github.com/traefik/traefik/compare/v2.11.11...v2.11.12)
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[middleware]** Bump github.com/klauspost/compress to dbd6c381492a ([#11162](https://github.com/traefik/traefik/pull/11162) by [kevinpollet](https://github.com/kevinpollet))
|
||||||
|
- **[webui]** Upgrade to node 22.9 and yarn lock to fix vulnerabilities ([#11173](https://github.com/traefik/traefik/pull/11173) by [kevinpollet](https://github.com/kevinpollet))
|
||||||
|
- **[webui]** Adopt a layout for the large amount of entrypoint port numbers ([#11157](https://github.com/traefik/traefik/pull/11157) by [framebassman](https://github.com/framebassman))
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- **[accesslogs]** Clarify that only header fields may be redacted in access-logs ([#11139](https://github.com/traefik/traefik/pull/11139) by [mattbnz](https://github.com/mattbnz))
|
||||||
|
- Update business callout ([#11172](https://github.com/traefik/traefik/pull/11172) by [tomatokoolaid](https://github.com/tomatokoolaid))
|
||||||
|
|
||||||
## [v3.2.0-rc1](https://github.com/traefik/traefik/tree/v3.2.0-rc1) (2024-10-02)
|
## [v3.2.0-rc1](https://github.com/traefik/traefik/tree/v3.2.0-rc1) (2024-10-02)
|
||||||
[All Commits](https://github.com/traefik/traefik/compare/v3.1.0-rc1...v3.2.0-rc1)
|
[All Commits](https://github.com/traefik/traefik/compare/v3.1.0-rc1...v3.2.0-rc1)
|
||||||
|
|
||||||
|
@ -32,6 +73,8 @@
|
||||||
**Bug fixes:**
|
**Bug fixes:**
|
||||||
- **[k8s/ingress,k8s]** Disable IngressClass lookup when disableClusterScopeResources is enabled ([#11111](https://github.com/traefik/traefik/pull/11111) by [jnoordsij](https://github.com/jnoordsij))
|
- **[k8s/ingress,k8s]** Disable IngressClass lookup when disableClusterScopeResources is enabled ([#11111](https://github.com/traefik/traefik/pull/11111) by [jnoordsij](https://github.com/jnoordsij))
|
||||||
- **[server]** Rework condition to not log on timeout ([#11132](https://github.com/traefik/traefik/pull/11132) by [rtribotte](https://github.com/rtribotte))
|
- **[server]** Rework condition to not log on timeout ([#11132](https://github.com/traefik/traefik/pull/11132) by [rtribotte](https://github.com/rtribotte))
|
||||||
|
|
||||||
|
**Misc:**
|
||||||
- Merge branch v2.11 into v3.1 ([#11149](https://github.com/traefik/traefik/pull/11149) by [kevinpollet](https://github.com/kevinpollet))
|
- Merge branch v2.11 into v3.1 ([#11149](https://github.com/traefik/traefik/pull/11149) by [kevinpollet](https://github.com/kevinpollet))
|
||||||
- Merge branch v2.11 into v3.1 ([#11142](https://github.com/traefik/traefik/pull/11142) by [rtribotte](https://github.com/rtribotte))
|
- Merge branch v2.11 into v3.1 ([#11142](https://github.com/traefik/traefik/pull/11142) by [rtribotte](https://github.com/rtribotte))
|
||||||
|
|
||||||
|
|
5
Makefile
5
Makefile
|
@ -1,13 +1,10 @@
|
||||||
SRCS = $(shell git ls-files '*.go' | grep -v '^vendor/')
|
SRCS = $(shell git ls-files '*.go' | grep -v '^vendor/')
|
||||||
|
|
||||||
TAG_NAME := $(shell git tag -l --contains HEAD)
|
TAG_NAME := $(shell git describe --abbrev=0 --tags --exact-match)
|
||||||
SHA := $(shell git rev-parse HEAD)
|
SHA := $(shell git rev-parse HEAD)
|
||||||
VERSION_GIT := $(if $(TAG_NAME),$(TAG_NAME),$(SHA))
|
VERSION_GIT := $(if $(TAG_NAME),$(TAG_NAME),$(SHA))
|
||||||
VERSION := $(if $(VERSION),$(VERSION),$(VERSION_GIT))
|
VERSION := $(if $(VERSION),$(VERSION),$(VERSION_GIT))
|
||||||
|
|
||||||
GIT_BRANCH := $(subst heads/,,$(shell git rev-parse --abbrev-ref HEAD 2>/dev/null))
|
|
||||||
|
|
||||||
REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]')
|
|
||||||
BIN_NAME := traefik
|
BIN_NAME := traefik
|
||||||
CODENAME ?= cheddar
|
CODENAME ?= cheddar
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,6 @@
|
||||||
Add API Gateway or API Management capabilities seamlessly to your existing Traefik deployments.
|
Add API Gateway or API Management capabilities seamlessly to your existing Traefik deployments.
|
||||||
No rip and replace. No learning curve.
|
No rip and replace. No learning curve.
|
||||||
|
|
||||||
- [Explore our API Gateway](https://traefik.io/traefik-hub-api-gateway/)
|
- [Explore our API Gateway](https://traefik.io/traefik-hub-api-gateway/) ([Watch the Demo Video](https://info.traefik.io/watch-traefik-api-gw-demo?cta=doc))
|
||||||
- [Explore our API Management](https://traefik.io/traefik-hub/)
|
- [Explore our API Management](https://traefik.io/traefik-hub/)
|
||||||
- [Get 24/7/365 Commercial Support for Traefik OSS](https://info.traefik.io/request-commercial-support)
|
- [Get 24/7/365 Commercial Support for Traefik OSS](https://info.traefik.io/request-commercial-support)
|
||||||
|
|
|
@ -78,6 +78,17 @@ Please use the `disableClusterScopeResources` option instead to avoid cluster sc
|
||||||
|
|
||||||
## v3.1 to v3.2
|
## v3.1 to v3.2
|
||||||
|
|
||||||
|
### Kubernetes CRD Provider
|
||||||
|
|
||||||
|
Starting with v3.2, the CRDs has been updated on [TraefikService](../routing/services#mirroring-service) (PR [#11032](https://github.com/traefik/traefik/pull/11032)) and on [RateLimit](../middlewares/http/ratelimit) & [InFlightReq](../middlewares/http/inflightreq) middlewares (PR [#9747](https://github.com/traefik/traefik/pull/9747)).
|
||||||
|
|
||||||
|
This update adds only new optional fields.
|
||||||
|
CRDs can be updated with this command:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.2/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml
|
||||||
|
```
|
||||||
|
|
||||||
### Kubernetes Gateway Provider Standard Channel
|
### Kubernetes Gateway Provider Standard Channel
|
||||||
|
|
||||||
Starting with v3.2, the Kubernetes Gateway Provider now supports [GRPCRoute](https://gateway-api.sigs.k8s.io/api-types/grpcroute/).
|
Starting with v3.2, the Kubernetes Gateway Provider now supports [GRPCRoute](https://gateway-api.sigs.k8s.io/api-types/grpcroute/).
|
||||||
|
|
|
@ -158,7 +158,8 @@ Each field can be set to:
|
||||||
|
|
||||||
- `keep` to keep the value
|
- `keep` to keep the value
|
||||||
- `drop` to drop the value
|
- `drop` to drop the value
|
||||||
- `redact` to replace the value with "redacted"
|
|
||||||
|
Header fields may also optionally be set to `redact` to replace the value with "REDACTED".
|
||||||
|
|
||||||
The `defaultMode` for `fields.names` is `keep`.
|
The `defaultMode` for `fields.names` is `keep`.
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,11 @@ description: "Learn how to use the Kubernetes Gateway API as a provider for conf
|
||||||
The Kubernetes Gateway provider is a Traefik implementation of the [Gateway API](https://gateway-api.sigs.k8s.io/)
|
The Kubernetes Gateway provider is a Traefik implementation of the [Gateway API](https://gateway-api.sigs.k8s.io/)
|
||||||
specification from the Kubernetes Special Interest Groups (SIGs).
|
specification from the Kubernetes Special Interest Groups (SIGs).
|
||||||
|
|
||||||
This provider supports Standard version [v1.1.0](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.1.0) of the Gateway API specification.
|
This provider supports Standard version [v1.2.0](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.2.0) of the Gateway API specification.
|
||||||
|
|
||||||
It fully supports all HTTP core and some extended features, as well as the `TCPRoute` and `TLSRoute` resources from the [Experimental channel](https://gateway-api.sigs.k8s.io/concepts/versioning/?h=#release-channels).
|
It fully supports all HTTP core and some extended features, as well as the `TCPRoute` and `TLSRoute` resources from the [Experimental channel](https://gateway-api.sigs.k8s.io/concepts/versioning/?h=#release-channels).
|
||||||
|
|
||||||
For more details, check out the conformance [report](https://github.com/kubernetes-sigs/gateway-api/tree/main/conformance/reports/v1.1.0/traefik-traefik).
|
For more details, check out the conformance [report](https://github.com/kubernetes-sigs/gateway-api/tree/main/conformance/reports/v1.2.0/traefik-traefik).
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,11 @@ description: "The Kubernetes Gateway API can be used as a provider for routing a
|
||||||
When using the Kubernetes Gateway API provider, Traefik leverages the Gateway API Custom Resource Definitions (CRDs) to obtain its routing configuration.
|
When using the Kubernetes Gateway API provider, Traefik leverages the Gateway API Custom Resource Definitions (CRDs) to obtain its routing configuration.
|
||||||
For detailed information on the Gateway API concepts and resources, refer to the official [documentation](https://gateway-api.sigs.k8s.io/).
|
For detailed information on the Gateway API concepts and resources, refer to the official [documentation](https://gateway-api.sigs.k8s.io/).
|
||||||
|
|
||||||
The Kubernetes Gateway API provider supports version [v1.1.0](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.1.0) of the specification.
|
The Kubernetes Gateway API provider supports version [v1.2.0](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.2.0) of the specification.
|
||||||
|
|
||||||
It fully supports all `HTTPRoute` core and some extended features, as well as the `TCPRoute` and `TLSRoute` resources from the [Experimental channel](https://gateway-api.sigs.k8s.io/concepts/versioning/?h=#release-channels).
|
It fully supports all `HTTPRoute` core and some extended features, like `GRPCRoute`, as well as the `TCPRoute` and `TLSRoute` resources from the [Experimental channel](https://gateway-api.sigs.k8s.io/concepts/versioning/?h=#release-channels).
|
||||||
|
|
||||||
For more details, check out the conformance [report](https://github.com/kubernetes-sigs/gateway-api/tree/main/conformance/reports/v1.1.0/traefik-traefik).
|
For more details, check out the conformance [report](https://github.com/kubernetes-sigs/gateway-api/tree/main/conformance/reports/v1.2.0/traefik-traefik).
|
||||||
|
|
||||||
## Deploying a Gateway
|
## Deploying a Gateway
|
||||||
|
|
||||||
|
|
13
go.mod
13
go.mod
|
@ -31,10 +31,10 @@ require (
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.7
|
github.com/hashicorp/go-retryablehttp v0.7.7
|
||||||
github.com/hashicorp/go-version v1.6.0
|
github.com/hashicorp/go-version v1.6.0
|
||||||
github.com/hashicorp/nomad/api v0.0.0-20240122103822-8a4bd61caf74 // No tag on the repo.
|
github.com/hashicorp/nomad/api v0.0.0-20240122103822-8a4bd61caf74 // No tag on the repo.
|
||||||
github.com/http-wasm/http-wasm-host-go v0.6.0
|
github.com/http-wasm/http-wasm-host-go v0.7.0
|
||||||
github.com/influxdata/influxdb-client-go/v2 v2.7.0
|
github.com/influxdata/influxdb-client-go/v2 v2.7.0
|
||||||
github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab // No tag on the repo.
|
github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab // No tag on the repo.
|
||||||
github.com/klauspost/compress v1.17.11-0.20240927175842-8e14b1b5a913 // Required to have the content-type fix: https://github.com/klauspost/compress/pull/1011
|
github.com/klauspost/compress v1.17.11-0.20241004063537-dbd6c381492a // Required to have the content-type fix: https://github.com/klauspost/compress/pull/1013
|
||||||
github.com/kvtools/consul v1.0.2
|
github.com/kvtools/consul v1.0.2
|
||||||
github.com/kvtools/etcdv3 v1.0.2
|
github.com/kvtools/etcdv3 v1.0.2
|
||||||
github.com/kvtools/redis v1.1.0
|
github.com/kvtools/redis v1.1.0
|
||||||
|
@ -61,7 +61,7 @@ require (
|
||||||
github.com/tailscale/tscert v0.0.0-20230806124524-28a91b69a046 // No tag on the repo.
|
github.com/tailscale/tscert v0.0.0-20230806124524-28a91b69a046 // No tag on the repo.
|
||||||
github.com/testcontainers/testcontainers-go v0.32.0
|
github.com/testcontainers/testcontainers-go v0.32.0
|
||||||
github.com/testcontainers/testcontainers-go/modules/k3s v0.32.0
|
github.com/testcontainers/testcontainers-go/modules/k3s v0.32.0
|
||||||
github.com/tetratelabs/wazero v1.7.2
|
github.com/tetratelabs/wazero v1.8.0
|
||||||
github.com/tidwall/gjson v1.17.0
|
github.com/tidwall/gjson v1.17.0
|
||||||
github.com/traefik/grpc-web v0.16.0
|
github.com/traefik/grpc-web v0.16.0
|
||||||
github.com/traefik/paerser v0.2.1
|
github.com/traefik/paerser v0.2.1
|
||||||
|
@ -86,6 +86,7 @@ require (
|
||||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // No tag on the repo.
|
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // No tag on the repo.
|
||||||
golang.org/x/mod v0.21.0
|
golang.org/x/mod v0.21.0
|
||||||
golang.org/x/net v0.29.0
|
golang.org/x/net v0.29.0
|
||||||
|
golang.org/x/sync v0.8.0
|
||||||
golang.org/x/sys v0.25.0
|
golang.org/x/sys v0.25.0
|
||||||
golang.org/x/text v0.18.0
|
golang.org/x/text v0.18.0
|
||||||
golang.org/x/time v0.5.0
|
golang.org/x/time v0.5.0
|
||||||
|
@ -100,7 +101,7 @@ require (
|
||||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // No tag on the repo.
|
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // No tag on the repo.
|
||||||
mvdan.cc/xurls/v2 v2.5.0
|
mvdan.cc/xurls/v2 v2.5.0
|
||||||
sigs.k8s.io/controller-runtime v0.18.0
|
sigs.k8s.io/controller-runtime v0.18.0
|
||||||
sigs.k8s.io/gateway-api v1.2.0-rc2
|
sigs.k8s.io/gateway-api v1.2.0
|
||||||
sigs.k8s.io/yaml v1.4.0
|
sigs.k8s.io/yaml v1.4.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -343,7 +344,6 @@ require (
|
||||||
golang.org/x/arch v0.4.0 // indirect
|
golang.org/x/arch v0.4.0 // indirect
|
||||||
golang.org/x/crypto v0.27.0 // indirect
|
golang.org/x/crypto v0.27.0 // indirect
|
||||||
golang.org/x/oauth2 v0.21.0 // indirect
|
golang.org/x/oauth2 v0.21.0 // indirect
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
|
||||||
golang.org/x/term v0.24.0 // indirect
|
golang.org/x/term v0.24.0 // indirect
|
||||||
google.golang.org/api v0.172.0 // indirect
|
google.golang.org/api v0.172.0 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
|
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||||
|
@ -374,6 +374,3 @@ replace (
|
||||||
// ambiguous import: found package github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http in multiple modules
|
// ambiguous import: found package github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http in multiple modules
|
||||||
// tencentcloud uses monorepo with multimodule but the go.mod files are incomplete.
|
// tencentcloud uses monorepo with multimodule but the go.mod files are incomplete.
|
||||||
exclude github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible
|
exclude github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible
|
||||||
|
|
||||||
// Replace to handle new wasmexport in official go and wazergo for http calls.
|
|
||||||
replace github.com/http-wasm/http-wasm-host-go => github.com/traefik/http-wasm-host-go v0.0.0-20240618100324-3c53dcaa1a70
|
|
||||||
|
|
16
go.sum
16
go.sum
|
@ -551,6 +551,8 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
|
||||||
github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=
|
github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=
|
||||||
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
|
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/http-wasm/http-wasm-host-go v0.7.0 h1:+1KrRyOO6tWiDB24QrtSYyDmzFLBBs3jioKaUT0mq1c=
|
||||||
|
github.com/http-wasm/http-wasm-host-go v0.7.0/go.mod h1:adXKcLmL7yuavH/e0kBAp7b3TgAHTo/enCduyN5bXGM=
|
||||||
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||||
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
||||||
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||||
|
@ -597,8 +599,8 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||||
github.com/klauspost/compress v1.17.11-0.20240927175842-8e14b1b5a913 h1:7s7Xd7zVElAw1qh/eh+tXDNfDNXXj38Tpq54eeG6/BM=
|
github.com/klauspost/compress v1.17.11-0.20241004063537-dbd6c381492a h1:cwHOqPB4H4iQq8177kf2SxpjNbcjJ2m3lNwKIe28Hqg=
|
||||||
github.com/klauspost/compress v1.17.11-0.20240927175842-8e14b1b5a913/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
github.com/klauspost/compress v1.17.11-0.20241004063537-dbd6c381492a/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
|
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
|
@ -995,8 +997,8 @@ github.com/testcontainers/testcontainers-go v0.32.0 h1:ug1aK08L3gCHdhknlTTwWjPHP
|
||||||
github.com/testcontainers/testcontainers-go v0.32.0/go.mod h1:CRHrzHLQhlXUsa5gXjTOfqIEJcrK5+xMDmBr/WMI88E=
|
github.com/testcontainers/testcontainers-go v0.32.0/go.mod h1:CRHrzHLQhlXUsa5gXjTOfqIEJcrK5+xMDmBr/WMI88E=
|
||||||
github.com/testcontainers/testcontainers-go/modules/k3s v0.32.0 h1:Z3DTMveNUqeGJZ+CXZhpvI7OF1BS71Ywi3SwoXLZ4Lc=
|
github.com/testcontainers/testcontainers-go/modules/k3s v0.32.0 h1:Z3DTMveNUqeGJZ+CXZhpvI7OF1BS71Ywi3SwoXLZ4Lc=
|
||||||
github.com/testcontainers/testcontainers-go/modules/k3s v0.32.0/go.mod h1:SYp1WtvNc3n/cg5atO6LvaOd2aqkQYMSDCcWPOUdaZg=
|
github.com/testcontainers/testcontainers-go/modules/k3s v0.32.0/go.mod h1:SYp1WtvNc3n/cg5atO6LvaOd2aqkQYMSDCcWPOUdaZg=
|
||||||
github.com/tetratelabs/wazero v1.7.2 h1:1+z5nXJNwMLPAWaTePFi49SSTL0IMx/i3Fg8Yc25GDc=
|
github.com/tetratelabs/wazero v1.8.0 h1:iEKu0d4c2Pd+QSRieYbnQC9yiFlMS9D+Jr0LsRmcF4g=
|
||||||
github.com/tetratelabs/wazero v1.7.2/go.mod h1:ytl6Zuh20R/eROuyDaGPkp82O9C/DJfXAwJfQ3X6/7Y=
|
github.com/tetratelabs/wazero v1.8.0/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs=
|
||||||
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
|
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
|
||||||
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||||
|
@ -1011,8 +1013,6 @@ github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9f
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/traefik/grpc-web v0.16.0 h1:eeUWZaFg6ZU0I9dWOYE2D5qkNzRBmXzzuRlxdltascY=
|
github.com/traefik/grpc-web v0.16.0 h1:eeUWZaFg6ZU0I9dWOYE2D5qkNzRBmXzzuRlxdltascY=
|
||||||
github.com/traefik/grpc-web v0.16.0/go.mod h1:2ttniSv7pTgBWIU2HZLokxRfFX3SA60c/DTmQQgVml4=
|
github.com/traefik/grpc-web v0.16.0/go.mod h1:2ttniSv7pTgBWIU2HZLokxRfFX3SA60c/DTmQQgVml4=
|
||||||
github.com/traefik/http-wasm-host-go v0.0.0-20240618100324-3c53dcaa1a70 h1:I+oBnV0orhmasb87yaX54tOAfqrV9+yKoQ1Cum5mq8w=
|
|
||||||
github.com/traefik/http-wasm-host-go v0.0.0-20240618100324-3c53dcaa1a70/go.mod h1:zQB3w+df4hryDEqBorGyA1DwPJ86LfKIASNLFuj6CuI=
|
|
||||||
github.com/traefik/paerser v0.2.1 h1:LFgeak1NmjEHF53c9ENdXdL1UMkF/lD5t+7Evsz4hH4=
|
github.com/traefik/paerser v0.2.1 h1:LFgeak1NmjEHF53c9ENdXdL1UMkF/lD5t+7Evsz4hH4=
|
||||||
github.com/traefik/paerser v0.2.1/go.mod h1:7BBDd4FANoVgaTZG+yh26jI6CA2nds7D/4VTEdIsh24=
|
github.com/traefik/paerser v0.2.1/go.mod h1:7BBDd4FANoVgaTZG+yh26jI6CA2nds7D/4VTEdIsh24=
|
||||||
github.com/traefik/yaegi v0.16.1 h1:f1De3DVJqIDKmnasUF6MwmWv1dSEEat0wcpXhD2On3E=
|
github.com/traefik/yaegi v0.16.1 h1:f1De3DVJqIDKmnasUF6MwmWv1dSEEat0wcpXhD2On3E=
|
||||||
|
@ -1542,8 +1542,8 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8
|
||||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
sigs.k8s.io/controller-runtime v0.18.0 h1:Z7jKuX784TQSUL1TIyeuF7j8KXZ4RtSX0YgtjKcSTME=
|
sigs.k8s.io/controller-runtime v0.18.0 h1:Z7jKuX784TQSUL1TIyeuF7j8KXZ4RtSX0YgtjKcSTME=
|
||||||
sigs.k8s.io/controller-runtime v0.18.0/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw=
|
sigs.k8s.io/controller-runtime v0.18.0/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw=
|
||||||
sigs.k8s.io/gateway-api v1.2.0-rc2 h1:v7V7JzaBuzwOLWWyyqlkqiqBi3ANBuZGV+uyyKzwmE8=
|
sigs.k8s.io/gateway-api v1.2.0 h1:LrToiFwtqKTKZcZtoQPTuo3FxhrrhTgzQG0Te+YGSo8=
|
||||||
sigs.k8s.io/gateway-api v1.2.0-rc2/go.mod h1:EpNfEXNjiYfUJypf0eZ0P5iXA9ekSGWaS1WgPaM42X0=
|
sigs.k8s.io/gateway-api v1.2.0/go.mod h1:EpNfEXNjiYfUJypf0eZ0P5iXA9ekSGWaS1WgPaM42X0=
|
||||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
apiVersion: gateway.networking.k8s.io/v1
|
apiVersion: gateway.networking.k8s.io/v1
|
||||||
date: '-'
|
date: '-'
|
||||||
gatewayAPIChannel: experimental
|
gatewayAPIChannel: experimental
|
||||||
gatewayAPIVersion: v1.2.0-rc2
|
gatewayAPIVersion: v1.2.0
|
||||||
implementation:
|
implementation:
|
||||||
contact:
|
contact:
|
||||||
- '@traefik/maintainers'
|
- '@traefik/maintainers'
|
|
@ -23,8 +23,8 @@ apiVersion: apiextensions.k8s.io/v1
|
||||||
kind: CustomResourceDefinition
|
kind: CustomResourceDefinition
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997
|
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328
|
||||||
gateway.networking.k8s.io/bundle-version: v1.2.0-rc2
|
gateway.networking.k8s.io/bundle-version: v1.2.0
|
||||||
gateway.networking.k8s.io/channel: experimental
|
gateway.networking.k8s.io/channel: experimental
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
|
@ -524,8 +524,8 @@ apiVersion: apiextensions.k8s.io/v1
|
||||||
kind: CustomResourceDefinition
|
kind: CustomResourceDefinition
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997
|
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328
|
||||||
gateway.networking.k8s.io/bundle-version: v1.2.0-rc2
|
gateway.networking.k8s.io/bundle-version: v1.2.0
|
||||||
gateway.networking.k8s.io/channel: experimental
|
gateway.networking.k8s.io/channel: experimental
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
|
@ -1153,8 +1153,8 @@ apiVersion: apiextensions.k8s.io/v1
|
||||||
kind: CustomResourceDefinition
|
kind: CustomResourceDefinition
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997
|
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328
|
||||||
gateway.networking.k8s.io/bundle-version: v1.2.0-rc2
|
gateway.networking.k8s.io/bundle-version: v1.2.0
|
||||||
gateway.networking.k8s.io/channel: experimental
|
gateway.networking.k8s.io/channel: experimental
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
name: gatewayclasses.gateway.networking.k8s.io
|
name: gatewayclasses.gateway.networking.k8s.io
|
||||||
|
@ -1673,8 +1673,8 @@ apiVersion: apiextensions.k8s.io/v1
|
||||||
kind: CustomResourceDefinition
|
kind: CustomResourceDefinition
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997
|
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328
|
||||||
gateway.networking.k8s.io/bundle-version: v1.2.0-rc2
|
gateway.networking.k8s.io/bundle-version: v1.2.0
|
||||||
gateway.networking.k8s.io/channel: experimental
|
gateway.networking.k8s.io/channel: experimental
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
name: gateways.gateway.networking.k8s.io
|
name: gateways.gateway.networking.k8s.io
|
||||||
|
@ -4089,8 +4089,8 @@ apiVersion: apiextensions.k8s.io/v1
|
||||||
kind: CustomResourceDefinition
|
kind: CustomResourceDefinition
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997
|
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328
|
||||||
gateway.networking.k8s.io/bundle-version: v1.2.0-rc2
|
gateway.networking.k8s.io/bundle-version: v1.2.0
|
||||||
gateway.networking.k8s.io/channel: experimental
|
gateway.networking.k8s.io/channel: experimental
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
name: grpcroutes.gateway.networking.k8s.io
|
name: grpcroutes.gateway.networking.k8s.io
|
||||||
|
@ -6327,8 +6327,8 @@ apiVersion: apiextensions.k8s.io/v1
|
||||||
kind: CustomResourceDefinition
|
kind: CustomResourceDefinition
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997
|
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328
|
||||||
gateway.networking.k8s.io/bundle-version: v1.2.0-rc2
|
gateway.networking.k8s.io/bundle-version: v1.2.0
|
||||||
gateway.networking.k8s.io/channel: experimental
|
gateway.networking.k8s.io/channel: experimental
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
name: httproutes.gateway.networking.k8s.io
|
name: httproutes.gateway.networking.k8s.io
|
||||||
|
@ -12489,8 +12489,8 @@ apiVersion: apiextensions.k8s.io/v1
|
||||||
kind: CustomResourceDefinition
|
kind: CustomResourceDefinition
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997
|
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328
|
||||||
gateway.networking.k8s.io/bundle-version: v1.2.0-rc2
|
gateway.networking.k8s.io/bundle-version: v1.2.0
|
||||||
gateway.networking.k8s.io/channel: experimental
|
gateway.networking.k8s.io/channel: experimental
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
name: referencegrants.gateway.networking.k8s.io
|
name: referencegrants.gateway.networking.k8s.io
|
||||||
|
@ -12682,8 +12682,8 @@ apiVersion: apiextensions.k8s.io/v1
|
||||||
kind: CustomResourceDefinition
|
kind: CustomResourceDefinition
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997
|
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328
|
||||||
gateway.networking.k8s.io/bundle-version: v1.2.0-rc2
|
gateway.networking.k8s.io/bundle-version: v1.2.0
|
||||||
gateway.networking.k8s.io/channel: experimental
|
gateway.networking.k8s.io/channel: experimental
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
name: tcproutes.gateway.networking.k8s.io
|
name: tcproutes.gateway.networking.k8s.io
|
||||||
|
@ -13427,8 +13427,8 @@ apiVersion: apiextensions.k8s.io/v1
|
||||||
kind: CustomResourceDefinition
|
kind: CustomResourceDefinition
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997
|
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328
|
||||||
gateway.networking.k8s.io/bundle-version: v1.2.0-rc2
|
gateway.networking.k8s.io/bundle-version: v1.2.0
|
||||||
gateway.networking.k8s.io/channel: experimental
|
gateway.networking.k8s.io/channel: experimental
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
name: tlsroutes.gateway.networking.k8s.io
|
name: tlsroutes.gateway.networking.k8s.io
|
||||||
|
@ -14235,8 +14235,8 @@ apiVersion: apiextensions.k8s.io/v1
|
||||||
kind: CustomResourceDefinition
|
kind: CustomResourceDefinition
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997
|
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328
|
||||||
gateway.networking.k8s.io/bundle-version: v1.2.0-rc2
|
gateway.networking.k8s.io/bundle-version: v1.2.0
|
||||||
gateway.networking.k8s.io/channel: experimental
|
gateway.networking.k8s.io/channel: experimental
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
name: udproutes.gateway.networking.k8s.io
|
name: udproutes.gateway.networking.k8s.io
|
|
@ -90,7 +90,7 @@ func (s *K8sConformanceSuite) SetupSuite() {
|
||||||
|
|
||||||
s.k3sContainer, err = k3s.Run(ctx,
|
s.k3sContainer, err = k3s.Run(ctx,
|
||||||
k3sImage,
|
k3sImage,
|
||||||
k3s.WithManifest("./fixtures/k8s-conformance/00-experimental-v1.2.0-rc2.yml"),
|
k3s.WithManifest("./fixtures/k8s-conformance/00-experimental-v1.2.0.yml"),
|
||||||
k3s.WithManifest("./fixtures/k8s-conformance/01-rbac.yml"),
|
k3s.WithManifest("./fixtures/k8s-conformance/01-rbac.yml"),
|
||||||
k3s.WithManifest("./fixtures/k8s-conformance/02-traefik.yml"),
|
k3s.WithManifest("./fixtures/k8s-conformance/02-traefik.yml"),
|
||||||
network.WithNetwork(nil, s.network),
|
network.WithNetwork(nil, s.network),
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/traefik/traefik/v3/pkg/middlewares/accesslog"
|
"github.com/traefik/traefik/v3/pkg/middlewares/accesslog"
|
||||||
"github.com/traefik/traefik/v3/pkg/middlewares/observability"
|
"github.com/traefik/traefik/v3/pkg/middlewares/observability"
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
"golang.org/x/sync/singleflight"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -26,6 +27,9 @@ type basicAuth struct {
|
||||||
headerField string
|
headerField string
|
||||||
removeHeader bool
|
removeHeader bool
|
||||||
name string
|
name string
|
||||||
|
|
||||||
|
checkSecret func(password, secret string) bool
|
||||||
|
singleflightGroup *singleflight.Group
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBasic creates a basicAuth middleware.
|
// NewBasic creates a basicAuth middleware.
|
||||||
|
@ -43,6 +47,8 @@ func NewBasic(ctx context.Context, next http.Handler, authConfig dynamic.BasicAu
|
||||||
headerField: authConfig.HeaderField,
|
headerField: authConfig.HeaderField,
|
||||||
removeHeader: authConfig.RemoveHeader,
|
removeHeader: authConfig.RemoveHeader,
|
||||||
name: name,
|
name: name,
|
||||||
|
checkSecret: goauth.CheckSecret,
|
||||||
|
singleflightGroup: new(singleflight.Group),
|
||||||
}
|
}
|
||||||
|
|
||||||
realm := defaultRealm
|
realm := defaultRealm
|
||||||
|
@ -64,10 +70,7 @@ func (b *basicAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
|
||||||
user, password, ok := req.BasicAuth()
|
user, password, ok := req.BasicAuth()
|
||||||
if ok {
|
if ok {
|
||||||
secret := b.auth.Secrets(user, b.auth.Realm)
|
ok = b.checkPassword(user, password)
|
||||||
if secret == "" || !goauth.CheckSecret(password, secret) {
|
|
||||||
ok = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logData := accesslog.GetLogData(req)
|
logData := accesslog.GetLogData(req)
|
||||||
|
@ -97,6 +100,20 @@ func (b *basicAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
b.next.ServeHTTP(rw, req)
|
b.next.ServeHTTP(rw, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *basicAuth) checkPassword(user, password string) bool {
|
||||||
|
secret := b.auth.Secrets(user, b.auth.Realm)
|
||||||
|
if secret == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
key := password + secret
|
||||||
|
match, _, _ := b.singleflightGroup.Do(key, func() (any, error) {
|
||||||
|
return b.checkSecret(password, secret), nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return match.(bool)
|
||||||
|
}
|
||||||
|
|
||||||
func (b *basicAuth) secretBasic(user, realm string) string {
|
func (b *basicAuth) secretBasic(user, realm string) string {
|
||||||
if secret, ok := b.users[user]; ok {
|
if secret, ok := b.users[user]; ok {
|
||||||
return secret
|
return secret
|
||||||
|
|
|
@ -7,7 +7,9 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -167,6 +169,50 @@ func TestBasicAuthHeaderPresent(t *testing.T) {
|
||||||
assert.Equal(t, "traefik\n", string(body))
|
assert.Equal(t, "traefik\n", string(body))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBasicAuthConcurrentHashOnce(t *testing.T) {
|
||||||
|
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprintln(w, "traefik")
|
||||||
|
})
|
||||||
|
auth := dynamic.BasicAuth{
|
||||||
|
Users: []string{"test:$2a$04$.8sTYfcxbSplCtoxt5TdJOgpBYkarKtZYsYfYxQ1edbYRuO1DNi0e"},
|
||||||
|
}
|
||||||
|
|
||||||
|
authMiddleware, err := NewBasic(context.Background(), next, auth, "authName")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
hashCount := 0
|
||||||
|
ba := authMiddleware.(*basicAuth)
|
||||||
|
ba.checkSecret = func(password, secret string) bool {
|
||||||
|
hashCount++
|
||||||
|
// delay to ensure the second request arrives
|
||||||
|
time.Sleep(time.Millisecond)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
ts := httptest.NewServer(authMiddleware)
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(2)
|
||||||
|
|
||||||
|
for range 2 {
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil)
|
||||||
|
req.SetBasicAuth("test", "test")
|
||||||
|
|
||||||
|
res, err := http.DefaultClient.Do(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, res.StatusCode, "they should be equal")
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
assert.Equal(t, 1, hashCount)
|
||||||
|
}
|
||||||
|
|
||||||
func TestBasicAuthUsersFromFile(t *testing.T) {
|
func TestBasicAuthUsersFromFile(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package compress
|
package compress
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"cmp"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -19,7 +20,7 @@ const (
|
||||||
|
|
||||||
type Encoding struct {
|
type Encoding struct {
|
||||||
Type string
|
Type string
|
||||||
Weight *float64
|
Weight float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCompressionEncoding(acceptEncoding []string, defaultEncoding string, supportedEncodings []string) string {
|
func getCompressionEncoding(acceptEncoding []string, defaultEncoding string, supportedEncodings []string) string {
|
||||||
|
@ -42,11 +43,11 @@ func getCompressionEncoding(acceptEncoding []string, defaultEncoding string, sup
|
||||||
|
|
||||||
encoding := encodings[0]
|
encoding := encodings[0]
|
||||||
|
|
||||||
if encoding.Type == identityName && encoding.Weight != nil && *encoding.Weight == 0 {
|
if encoding.Type == identityName && encoding.Weight == 0 {
|
||||||
return notAcceptable
|
return notAcceptable
|
||||||
}
|
}
|
||||||
|
|
||||||
if encoding.Type == wildcardName && encoding.Weight != nil && *encoding.Weight == 0 {
|
if encoding.Type == wildcardName && encoding.Weight == 0 {
|
||||||
return notAcceptable
|
return notAcceptable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,11 +88,13 @@ func parseAcceptEncoding(acceptEncoding, supportedEncodings []string) ([]Encodin
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var weight *float64
|
// If no "q" parameter is present, the default weight is 1.
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc9110.html#name-quality-values
|
||||||
|
weight := 1.0
|
||||||
if len(parsed) > 1 && strings.HasPrefix(parsed[1], "q=") {
|
if len(parsed) > 1 && strings.HasPrefix(parsed[1], "q=") {
|
||||||
w, _ := strconv.ParseFloat(strings.TrimPrefix(parsed[1], "q="), 64)
|
w, _ := strconv.ParseFloat(strings.TrimPrefix(parsed[1], "q="), 64)
|
||||||
|
|
||||||
weight = &w
|
weight = w
|
||||||
hasWeight = true
|
hasWeight = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,41 +105,9 @@ func parseAcceptEncoding(acceptEncoding, supportedEncodings []string) ([]Encodin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
slices.SortFunc(encodings, compareEncoding)
|
slices.SortFunc(encodings, func(a, b Encoding) int {
|
||||||
|
return cmp.Compare(b.Weight, a.Weight)
|
||||||
|
})
|
||||||
|
|
||||||
return encodings, hasWeight
|
return encodings, hasWeight
|
||||||
}
|
}
|
||||||
|
|
||||||
func compareEncoding(a, b Encoding) int {
|
|
||||||
lhs, rhs := a.Weight, b.Weight
|
|
||||||
|
|
||||||
if lhs == nil && rhs == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if lhs == nil && *rhs == 0 {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
if lhs == nil {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if rhs == nil && *lhs == 0 {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if rhs == nil {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
if *lhs < *rhs {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if *lhs > *rhs {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
|
@ -87,6 +87,12 @@ func Test_getCompressionEncoding(t *testing.T) {
|
||||||
supportedEncodings: []string{zstdName, brotliName},
|
supportedEncodings: []string{zstdName, brotliName},
|
||||||
expected: brotliName,
|
expected: brotliName,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "mixed weight",
|
||||||
|
acceptEncoding: []string{"gzip, br;q=0.9"},
|
||||||
|
supportedEncodings: []string{gzipName, brotliName},
|
||||||
|
expected: gzipName,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
|
@ -116,10 +122,10 @@ func Test_parseAcceptEncoding(t *testing.T) {
|
||||||
desc: "weight",
|
desc: "weight",
|
||||||
values: []string{"br;q=1.0, zstd;q=0.9, gzip;q=0.8, *;q=0.1"},
|
values: []string{"br;q=1.0, zstd;q=0.9, gzip;q=0.8, *;q=0.1"},
|
||||||
expected: []Encoding{
|
expected: []Encoding{
|
||||||
{Type: brotliName, Weight: ptr[float64](1)},
|
{Type: brotliName, Weight: 1},
|
||||||
{Type: zstdName, Weight: ptr(0.9)},
|
{Type: zstdName, Weight: 0.9},
|
||||||
{Type: gzipName, Weight: ptr(0.8)},
|
{Type: gzipName, Weight: 0.8},
|
||||||
{Type: wildcardName, Weight: ptr(0.1)},
|
{Type: wildcardName, Weight: 0.1},
|
||||||
},
|
},
|
||||||
assertWeight: assert.True,
|
assertWeight: assert.True,
|
||||||
},
|
},
|
||||||
|
@ -128,9 +134,9 @@ func Test_parseAcceptEncoding(t *testing.T) {
|
||||||
values: []string{"br;q=1.0, zstd;q=0.9, gzip;q=0.8, *;q=0.1"},
|
values: []string{"br;q=1.0, zstd;q=0.9, gzip;q=0.8, *;q=0.1"},
|
||||||
supportedEncodings: []string{brotliName, gzipName},
|
supportedEncodings: []string{brotliName, gzipName},
|
||||||
expected: []Encoding{
|
expected: []Encoding{
|
||||||
{Type: brotliName, Weight: ptr[float64](1)},
|
{Type: brotliName, Weight: 1},
|
||||||
{Type: gzipName, Weight: ptr(0.8)},
|
{Type: gzipName, Weight: 0.8},
|
||||||
{Type: wildcardName, Weight: ptr(0.1)},
|
{Type: wildcardName, Weight: 0.1},
|
||||||
},
|
},
|
||||||
assertWeight: assert.True,
|
assertWeight: assert.True,
|
||||||
},
|
},
|
||||||
|
@ -138,10 +144,10 @@ func Test_parseAcceptEncoding(t *testing.T) {
|
||||||
desc: "mixed",
|
desc: "mixed",
|
||||||
values: []string{"zstd,gzip, br;q=1.0, *;q=0"},
|
values: []string{"zstd,gzip, br;q=1.0, *;q=0"},
|
||||||
expected: []Encoding{
|
expected: []Encoding{
|
||||||
{Type: brotliName, Weight: ptr[float64](1)},
|
{Type: zstdName, Weight: 1},
|
||||||
{Type: zstdName},
|
{Type: gzipName, Weight: 1},
|
||||||
{Type: gzipName},
|
{Type: brotliName, Weight: 1},
|
||||||
{Type: wildcardName, Weight: ptr[float64](0)},
|
{Type: wildcardName, Weight: 0},
|
||||||
},
|
},
|
||||||
assertWeight: assert.True,
|
assertWeight: assert.True,
|
||||||
},
|
},
|
||||||
|
@ -150,8 +156,8 @@ func Test_parseAcceptEncoding(t *testing.T) {
|
||||||
values: []string{"zstd,gzip, br;q=1.0, *;q=0"},
|
values: []string{"zstd,gzip, br;q=1.0, *;q=0"},
|
||||||
supportedEncodings: []string{zstdName},
|
supportedEncodings: []string{zstdName},
|
||||||
expected: []Encoding{
|
expected: []Encoding{
|
||||||
{Type: zstdName},
|
{Type: zstdName, Weight: 1},
|
||||||
{Type: wildcardName, Weight: ptr[float64](0)},
|
{Type: wildcardName, Weight: 0},
|
||||||
},
|
},
|
||||||
assertWeight: assert.True,
|
assertWeight: assert.True,
|
||||||
},
|
},
|
||||||
|
@ -159,10 +165,10 @@ func Test_parseAcceptEncoding(t *testing.T) {
|
||||||
desc: "no weight",
|
desc: "no weight",
|
||||||
values: []string{"zstd, gzip, br, *"},
|
values: []string{"zstd, gzip, br, *"},
|
||||||
expected: []Encoding{
|
expected: []Encoding{
|
||||||
{Type: zstdName},
|
{Type: zstdName, Weight: 1},
|
||||||
{Type: gzipName},
|
{Type: gzipName, Weight: 1},
|
||||||
{Type: brotliName},
|
{Type: brotliName, Weight: 1},
|
||||||
{Type: wildcardName},
|
{Type: wildcardName, Weight: 1},
|
||||||
},
|
},
|
||||||
assertWeight: assert.False,
|
assertWeight: assert.False,
|
||||||
},
|
},
|
||||||
|
@ -171,8 +177,8 @@ func Test_parseAcceptEncoding(t *testing.T) {
|
||||||
values: []string{"zstd, gzip, br, *"},
|
values: []string{"zstd, gzip, br, *"},
|
||||||
supportedEncodings: []string{"gzip"},
|
supportedEncodings: []string{"gzip"},
|
||||||
expected: []Encoding{
|
expected: []Encoding{
|
||||||
{Type: gzipName},
|
{Type: gzipName, Weight: 1},
|
||||||
{Type: wildcardName},
|
{Type: wildcardName, Weight: 1},
|
||||||
},
|
},
|
||||||
assertWeight: assert.False,
|
assertWeight: assert.False,
|
||||||
},
|
},
|
||||||
|
@ -180,9 +186,9 @@ func Test_parseAcceptEncoding(t *testing.T) {
|
||||||
desc: "weight and identity",
|
desc: "weight and identity",
|
||||||
values: []string{"gzip;q=1.0, identity; q=0.5, *;q=0"},
|
values: []string{"gzip;q=1.0, identity; q=0.5, *;q=0"},
|
||||||
expected: []Encoding{
|
expected: []Encoding{
|
||||||
{Type: gzipName, Weight: ptr[float64](1)},
|
{Type: gzipName, Weight: 1},
|
||||||
{Type: identityName, Weight: ptr(0.5)},
|
{Type: identityName, Weight: 0.5},
|
||||||
{Type: wildcardName, Weight: ptr[float64](0)},
|
{Type: wildcardName, Weight: 0},
|
||||||
},
|
},
|
||||||
assertWeight: assert.True,
|
assertWeight: assert.True,
|
||||||
},
|
},
|
||||||
|
@ -191,8 +197,8 @@ func Test_parseAcceptEncoding(t *testing.T) {
|
||||||
values: []string{"gzip;q=1.0, identity; q=0.5, *;q=0"},
|
values: []string{"gzip;q=1.0, identity; q=0.5, *;q=0"},
|
||||||
supportedEncodings: []string{"br"},
|
supportedEncodings: []string{"br"},
|
||||||
expected: []Encoding{
|
expected: []Encoding{
|
||||||
{Type: identityName, Weight: ptr(0.5)},
|
{Type: identityName, Weight: 0.5},
|
||||||
{Type: wildcardName, Weight: ptr[float64](0)},
|
{Type: wildcardName, Weight: 0},
|
||||||
},
|
},
|
||||||
assertWeight: assert.True,
|
assertWeight: assert.True,
|
||||||
},
|
},
|
||||||
|
@ -213,7 +219,3 @@ func Test_parseAcceptEncoding(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ptr[T any](t T) *T {
|
|
||||||
return &t
|
|
||||||
}
|
|
||||||
|
|
|
@ -688,39 +688,32 @@ func Test1xxResponses(t *testing.T) {
|
||||||
assert.NotEqualValues(t, body, fakeBody)
|
assert.NotEqualValues(t, body, fakeBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkCompress(b *testing.B) {
|
func BenchmarkCompressGzip(b *testing.B) {
|
||||||
|
runCompressionBenchmark(b, gzipName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkCompressBrotli(b *testing.B) {
|
||||||
|
runCompressionBenchmark(b, brotliName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkCompressZstandard(b *testing.B) {
|
||||||
|
runCompressionBenchmark(b, zstdName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runCompressionBenchmark(b *testing.B, algorithm string) {
|
||||||
|
b.Helper()
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
parallel bool
|
parallel bool
|
||||||
size int
|
size int
|
||||||
}{
|
}{
|
||||||
{
|
{"2k", false, 2048},
|
||||||
name: "2k",
|
{"20k", false, 20480},
|
||||||
size: 2048,
|
{"100k", false, 102400},
|
||||||
},
|
{"2k parallel", true, 2048},
|
||||||
{
|
{"20k parallel", true, 20480},
|
||||||
name: "20k",
|
{"100k parallel", true, 102400},
|
||||||
size: 20480,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "100k",
|
|
||||||
size: 102400,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "2k parallel",
|
|
||||||
parallel: true,
|
|
||||||
size: 2048,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "20k parallel",
|
|
||||||
parallel: true,
|
|
||||||
size: 20480,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "100k parallel",
|
|
||||||
parallel: true,
|
|
||||||
size: 102400,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
|
@ -734,7 +727,7 @@ func BenchmarkCompress(b *testing.B) {
|
||||||
handler, _ := New(context.Background(), next, dynamic.Compress{}, "testing")
|
handler, _ := New(context.Background(), next, dynamic.Compress{}, "testing")
|
||||||
|
|
||||||
req, _ := http.NewRequest(http.MethodGet, "/whatever", nil)
|
req, _ := http.NewRequest(http.MethodGet, "/whatever", nil)
|
||||||
req.Header.Set("Accept-Encoding", "gzip")
|
req.Header.Set("Accept-Encoding", algorithm)
|
||||||
|
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.SetBytes(int64(test.size))
|
b.SetBytes(int64(test.size))
|
||||||
|
@ -742,7 +735,7 @@ func BenchmarkCompress(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
runBenchmark(b, req, handler)
|
runBenchmark(b, req, handler, algorithm)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
@ -750,13 +743,13 @@ func BenchmarkCompress(b *testing.B) {
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for range b.N {
|
for range b.N {
|
||||||
runBenchmark(b, req, handler)
|
runBenchmark(b, req, handler, algorithm)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runBenchmark(b *testing.B, req *http.Request, handler http.Handler) {
|
func runBenchmark(b *testing.B, req *http.Request, handler http.Handler, algorithm string) {
|
||||||
b.Helper()
|
b.Helper()
|
||||||
|
|
||||||
res := httptest.NewRecorder()
|
res := httptest.NewRecorder()
|
||||||
|
@ -765,7 +758,7 @@ func runBenchmark(b *testing.B, req *http.Request, handler http.Handler) {
|
||||||
b.Fatalf("Expected 200 but got %d", code)
|
b.Fatalf("Expected 200 but got %d", code)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(b, gzipName, res.Header().Get(contentEncodingHeader))
|
assert.Equal(b, algorithm, res.Header().Get(contentEncodingHeader))
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateBytes(length int) []byte {
|
func generateBytes(length int) []byte {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"mime"
|
"mime"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/andybalholm/brotli"
|
"github.com/andybalholm/brotli"
|
||||||
"github.com/klauspost/compress/zstd"
|
"github.com/klauspost/compress/zstd"
|
||||||
|
@ -45,6 +46,7 @@ type CompressionHandler struct {
|
||||||
excludedContentTypes []parsedContentType
|
excludedContentTypes []parsedContentType
|
||||||
includedContentTypes []parsedContentType
|
includedContentTypes []parsedContentType
|
||||||
next http.Handler
|
next http.Handler
|
||||||
|
writerPool sync.Pool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCompressionHandler returns a new compressing handler.
|
// NewCompressionHandler returns a new compressing handler.
|
||||||
|
@ -92,7 +94,7 @@ func NewCompressionHandler(cfg Config, next http.Handler) (http.Handler, error)
|
||||||
func (c *CompressionHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
func (c *CompressionHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
rw.Header().Add(vary, acceptEncoding)
|
rw.Header().Add(vary, acceptEncoding)
|
||||||
|
|
||||||
compressionWriter, err := newCompressionWriter(c.cfg.Algorithm, rw)
|
compressionWriter, err := c.getCompressionWriter(rw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger := middlewares.GetLogger(r.Context(), c.cfg.MiddlewareName, typeName)
|
logger := middlewares.GetLogger(r.Context(), c.cfg.MiddlewareName, typeName)
|
||||||
logger.Debug().Msgf("Create compression handler: %v", err)
|
logger.Debug().Msgf("Create compression handler: %v", err)
|
||||||
|
@ -100,6 +102,7 @@ func (c *CompressionHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request)
|
||||||
rw.WriteHeader(http.StatusInternalServerError)
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer c.putCompressionWriter(compressionWriter)
|
||||||
|
|
||||||
responseWriter := &responseWriter{
|
responseWriter := &responseWriter{
|
||||||
rw: rw,
|
rw: rw,
|
||||||
|
@ -130,6 +133,8 @@ type compression interface {
|
||||||
// as it would otherwise send some extra "end of compression" bytes.
|
// as it would otherwise send some extra "end of compression" bytes.
|
||||||
// Close also makes sure to flush whatever was left to write from the buffer.
|
// Close also makes sure to flush whatever was left to write from the buffer.
|
||||||
Close() error
|
Close() error
|
||||||
|
// Reset reinitializes the state of the encoder, allowing it to be reused.
|
||||||
|
Reset(w io.Writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
type compressionWriter struct {
|
type compressionWriter struct {
|
||||||
|
@ -137,6 +142,19 @@ type compressionWriter struct {
|
||||||
alg string
|
alg string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CompressionHandler) getCompressionWriter(rw io.Writer) (*compressionWriter, error) {
|
||||||
|
if writer, ok := c.writerPool.Get().(*compressionWriter); ok {
|
||||||
|
writer.compression.Reset(rw)
|
||||||
|
return writer, nil
|
||||||
|
}
|
||||||
|
return newCompressionWriter(c.cfg.Algorithm, rw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CompressionHandler) putCompressionWriter(writer *compressionWriter) {
|
||||||
|
writer.Reset(nil)
|
||||||
|
c.writerPool.Put(writer)
|
||||||
|
}
|
||||||
|
|
||||||
func newCompressionWriter(algo string, in io.Writer) (*compressionWriter, error) {
|
func newCompressionWriter(algo string, in io.Writer) (*compressionWriter, error) {
|
||||||
switch algo {
|
switch algo {
|
||||||
case brotliName:
|
case brotliName:
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/http-wasm/http-wasm-host-go/handler"
|
"github.com/http-wasm/http-wasm-host-go/handler"
|
||||||
|
@ -135,7 +136,19 @@ func (b *wasmMiddlewareBuilder) buildMiddleware(ctx context.Context, next http.H
|
||||||
return nil, nil, fmt.Errorf("creating middleware: %w", err)
|
return nil, nil, fmt.Errorf("creating middleware: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return mw.NewHandler(ctx, next), applyCtx, nil
|
h := mw.NewHandler(ctx, next)
|
||||||
|
|
||||||
|
// Traefik does not Close the middleware when creating a new instance on a configuration change.
|
||||||
|
// When the middleware is marked to be GC, we need to close it so the wasm instance is properly closed.
|
||||||
|
// Reference: https://github.com/traefik/traefik/issues/11119
|
||||||
|
runtime.SetFinalizer(h, func(_ http.Handler) {
|
||||||
|
if err := mw.Close(ctx); err != nil {
|
||||||
|
logger.Err(err).Msg("[wasm] middleware Close failed")
|
||||||
|
} else {
|
||||||
|
logger.Debug().Msg("[wasm] middleware Close ok")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return h, applyCtx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WasmMiddleware is an HTTP handler plugin wrapper.
|
// WasmMiddleware is an HTTP handler plugin wrapper.
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
---
|
||||||
|
apiVersion: gateway.networking.k8s.io/v1
|
||||||
|
kind: GatewayClass
|
||||||
|
metadata:
|
||||||
|
name: traefik-internal
|
||||||
|
labels:
|
||||||
|
name: traefik-internal
|
||||||
|
spec:
|
||||||
|
controllerName: traefik.io/gateway-controller
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: gateway.networking.k8s.io/v1
|
||||||
|
kind: Gateway
|
||||||
|
metadata:
|
||||||
|
name: traefik-internal
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
gatewayClassName: traefik-internal
|
||||||
|
listeners:
|
||||||
|
- name: http
|
||||||
|
protocol: HTTP
|
||||||
|
port: 9080
|
||||||
|
allowedRoutes:
|
||||||
|
namespaces:
|
||||||
|
from: Same
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: gateway.networking.k8s.io/v1
|
||||||
|
kind: GatewayClass
|
||||||
|
metadata:
|
||||||
|
name: traefik-external
|
||||||
|
labels:
|
||||||
|
name: traefik-external
|
||||||
|
spec:
|
||||||
|
controllerName: traefik.io/gateway-controller
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: gateway.networking.k8s.io/v1
|
||||||
|
kind: Gateway
|
||||||
|
metadata:
|
||||||
|
name: traefik-external
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
gatewayClassName: traefik-external
|
||||||
|
listeners:
|
||||||
|
- name: http
|
||||||
|
protocol: HTTP
|
||||||
|
port: 9080
|
||||||
|
allowedRoutes:
|
||||||
|
namespaces:
|
||||||
|
from: Same
|
|
@ -96,6 +96,7 @@ spec:
|
||||||
- name: web
|
- name: web
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
port: 8080
|
port: 8080
|
||||||
|
appProtocol: http
|
||||||
targetPort: web
|
targetPort: web
|
||||||
selector:
|
selector:
|
||||||
app: containous
|
app: containous
|
||||||
|
@ -131,6 +132,8 @@ metadata:
|
||||||
spec:
|
spec:
|
||||||
ports:
|
ports:
|
||||||
- name: websecure
|
- name: websecure
|
||||||
|
protocol: TCP
|
||||||
|
appProtocol: https
|
||||||
port: 443
|
port: 443
|
||||||
targetPort: websecure
|
targetPort: websecure
|
||||||
selector:
|
selector:
|
||||||
|
|
|
@ -2,6 +2,7 @@ package gateway
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -32,6 +33,11 @@ func (p *Provider) loadGRPCRoutes(ctx context.Context, gatewayListeners []gatewa
|
||||||
Str("namespace", route.Namespace).
|
Str("namespace", route.Namespace).
|
||||||
Logger()
|
Logger()
|
||||||
|
|
||||||
|
routeListeners := matchingGatewayListeners(gatewayListeners, route.Namespace, route.Spec.ParentRefs)
|
||||||
|
if len(routeListeners) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
var parentStatuses []gatev1.RouteParentStatus
|
var parentStatuses []gatev1.RouteParentStatus
|
||||||
for _, parentRef := range route.Spec.ParentRefs {
|
for _, parentRef := range route.Spec.ParentRefs {
|
||||||
parentStatus := &gatev1.RouteParentStatus{
|
parentStatus := &gatev1.RouteParentStatus{
|
||||||
|
@ -48,11 +54,9 @@ func (p *Provider) loadGRPCRoutes(ctx context.Context, gatewayListeners []gatewa
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, listener := range gatewayListeners {
|
for _, listener := range routeListeners {
|
||||||
accepted := true
|
accepted := matchListener(listener, parentRef)
|
||||||
if !matchListener(listener, route.Namespace, parentRef) {
|
|
||||||
accepted = false
|
|
||||||
}
|
|
||||||
if accepted && !allowRoute(listener, route.Namespace, kindGRPCRoute) {
|
if accepted && !allowRoute(listener, route.Namespace, kindGRPCRoute) {
|
||||||
parentStatus.Conditions = updateRouteConditionAccepted(parentStatus.Conditions, string(gatev1.RouteReasonNotAllowedByListeners))
|
parentStatus.Conditions = updateRouteConditionAccepted(parentStatus.Conditions, string(gatev1.RouteReasonNotAllowedByListeners))
|
||||||
accepted = false
|
accepted = false
|
||||||
|
@ -334,14 +338,15 @@ func (p *Provider) loadGRPCServers(namespace string, route *gatev1.GRPCRoute, ba
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if svcPort.AppProtocol != nil && *svcPort.AppProtocol != appProtocolH2C {
|
protocol, err := getGRPCServiceProtocol(svcPort)
|
||||||
|
if err != nil {
|
||||||
return nil, &metav1.Condition{
|
return nil, &metav1.Condition{
|
||||||
Type: string(gatev1.RouteConditionResolvedRefs),
|
Type: string(gatev1.RouteConditionResolvedRefs),
|
||||||
Status: metav1.ConditionFalse,
|
Status: metav1.ConditionFalse,
|
||||||
ObservedGeneration: route.Generation,
|
ObservedGeneration: route.Generation,
|
||||||
LastTransitionTime: metav1.Now(),
|
LastTransitionTime: metav1.Now(),
|
||||||
Reason: string(gatev1.RouteReasonUnsupportedProtocol),
|
Reason: string(gatev1.RouteReasonUnsupportedProtocol),
|
||||||
Message: fmt.Sprintf("Cannot load GRPCBackendRef %s/%s: only kubernetes.io/h2c appProtocol is supported", namespace, backendRef.Name),
|
Message: fmt.Sprintf("Cannot load GRPCBackendRef %s/%s: only \"kubernetes.io/h2c\" and \"https\" appProtocol is supported", namespace, backendRef.Name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,7 +355,7 @@ func (p *Provider) loadGRPCServers(namespace string, route *gatev1.GRPCRoute, ba
|
||||||
|
|
||||||
for _, ba := range backendAddresses {
|
for _, ba := range backendAddresses {
|
||||||
lb.Servers = append(lb.Servers, dynamic.Server{
|
lb.Servers = append(lb.Servers, dynamic.Server{
|
||||||
URL: fmt.Sprintf("h2c://%s", net.JoinHostPort(ba.IP, strconv.Itoa(int(ba.Port)))),
|
URL: fmt.Sprintf("%s://%s", protocol, net.JoinHostPort(ba.IP, strconv.Itoa(int(ba.Port)))),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return lb, nil
|
return lb, nil
|
||||||
|
@ -405,3 +410,22 @@ func buildGRPCHeaderRules(headers []gatev1.GRPCHeaderMatch) []string {
|
||||||
|
|
||||||
return rules
|
return rules
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getGRPCServiceProtocol(portSpec corev1.ServicePort) (string, error) {
|
||||||
|
if portSpec.Protocol != corev1.ProtocolTCP {
|
||||||
|
return "", errors.New("only TCP protocol is supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
if portSpec.AppProtocol == nil {
|
||||||
|
return schemeH2C, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ap := *portSpec.AppProtocol; ap {
|
||||||
|
case appProtocolH2C:
|
||||||
|
return schemeH2C, nil
|
||||||
|
case appProtocolHTTPS:
|
||||||
|
return schemeHTTPS, nil
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("unsupported application protocol %s", ap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -36,6 +36,11 @@ func (p *Provider) loadHTTPRoutes(ctx context.Context, gatewayListeners []gatewa
|
||||||
Str("namespace", route.Namespace).
|
Str("namespace", route.Namespace).
|
||||||
Logger()
|
Logger()
|
||||||
|
|
||||||
|
routeListeners := matchingGatewayListeners(gatewayListeners, route.Namespace, route.Spec.ParentRefs)
|
||||||
|
if len(routeListeners) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
var parentStatuses []gatev1.RouteParentStatus
|
var parentStatuses []gatev1.RouteParentStatus
|
||||||
for _, parentRef := range route.Spec.ParentRefs {
|
for _, parentRef := range route.Spec.ParentRefs {
|
||||||
parentStatus := &gatev1.RouteParentStatus{
|
parentStatus := &gatev1.RouteParentStatus{
|
||||||
|
@ -52,11 +57,9 @@ func (p *Provider) loadHTTPRoutes(ctx context.Context, gatewayListeners []gatewa
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, listener := range gatewayListeners {
|
for _, listener := range routeListeners {
|
||||||
accepted := true
|
accepted := matchListener(listener, parentRef)
|
||||||
if !matchListener(listener, route.Namespace, parentRef) {
|
|
||||||
accepted = false
|
|
||||||
}
|
|
||||||
if accepted && !allowRoute(listener, route.Namespace, kindHTTPRoute) {
|
if accepted && !allowRoute(listener, route.Namespace, kindHTTPRoute) {
|
||||||
parentStatus.Conditions = updateRouteConditionAccepted(parentStatus.Conditions, string(gatev1.RouteReasonNotAllowedByListeners))
|
parentStatus.Conditions = updateRouteConditionAccepted(parentStatus.Conditions, string(gatev1.RouteReasonNotAllowedByListeners))
|
||||||
accepted = false
|
accepted = false
|
||||||
|
@ -465,7 +468,7 @@ func (p *Provider) loadHTTPServers(namespace string, route *gatev1.HTTPRoute, ba
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol, err := getProtocol(svcPort)
|
protocol, err := getHTTPServiceProtocol(svcPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, corev1.ServicePort{}, &metav1.Condition{
|
return nil, corev1.ServicePort{}, &metav1.Condition{
|
||||||
Type: string(gatev1.RouteConditionResolvedRefs),
|
Type: string(gatev1.RouteConditionResolvedRefs),
|
||||||
|
@ -718,7 +721,7 @@ func createRequestRedirect(filter *gatev1.HTTPRequestRedirectFilter, pathMatch g
|
||||||
|
|
||||||
var port *string
|
var port *string
|
||||||
filterScheme := ptr.Deref(filter.Scheme, "")
|
filterScheme := ptr.Deref(filter.Scheme, "")
|
||||||
if filterScheme == "http" || filterScheme == "https" {
|
if filterScheme == schemeHTTP || filterScheme == schemeHTTPS {
|
||||||
port = ptr.To("")
|
port = ptr.To("")
|
||||||
}
|
}
|
||||||
if filter.Port != nil {
|
if filter.Port != nil {
|
||||||
|
@ -780,26 +783,26 @@ func createURLRewrite(filter *gatev1.HTTPURLRewriteFilter, pathMatch gatev1.HTTP
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getProtocol(portSpec corev1.ServicePort) (string, error) {
|
func getHTTPServiceProtocol(portSpec corev1.ServicePort) (string, error) {
|
||||||
if portSpec.Protocol != corev1.ProtocolTCP {
|
if portSpec.Protocol != corev1.ProtocolTCP {
|
||||||
return "", errors.New("only TCP protocol is supported")
|
return "", errors.New("only TCP protocol is supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
if portSpec.AppProtocol == nil {
|
if portSpec.AppProtocol == nil {
|
||||||
protocol := "http"
|
protocol := schemeHTTP
|
||||||
if portSpec.Port == 443 || strings.HasPrefix(portSpec.Name, "https") {
|
if portSpec.Port == 443 || strings.HasPrefix(portSpec.Name, schemeHTTPS) {
|
||||||
protocol = "https"
|
protocol = schemeHTTPS
|
||||||
}
|
}
|
||||||
return protocol, nil
|
return protocol, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ap := *portSpec.AppProtocol; ap {
|
switch ap := *portSpec.AppProtocol; ap {
|
||||||
case appProtocolH2C:
|
case appProtocolH2C:
|
||||||
return "h2c", nil
|
return schemeH2C, nil
|
||||||
case appProtocolWS:
|
case appProtocolHTTP, appProtocolWS:
|
||||||
return "http", nil
|
return schemeHTTP, nil
|
||||||
case appProtocolWSS:
|
case appProtocolHTTPS, appProtocolWSS:
|
||||||
return "https", nil
|
return schemeHTTPS, nil
|
||||||
default:
|
default:
|
||||||
return "", fmt.Errorf("unsupported application protocol %s", ap)
|
return "", fmt.Errorf("unsupported application protocol %s", ap)
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,9 +50,15 @@ const (
|
||||||
kindTLSRoute = "TLSRoute"
|
kindTLSRoute = "TLSRoute"
|
||||||
kindService = "Service"
|
kindService = "Service"
|
||||||
|
|
||||||
|
appProtocolHTTP = "http"
|
||||||
|
appProtocolHTTPS = "https"
|
||||||
appProtocolH2C = "kubernetes.io/h2c"
|
appProtocolH2C = "kubernetes.io/h2c"
|
||||||
appProtocolWS = "kubernetes.io/ws"
|
appProtocolWS = "kubernetes.io/ws"
|
||||||
appProtocolWSS = "kubernetes.io/wss"
|
appProtocolWSS = "kubernetes.io/wss"
|
||||||
|
|
||||||
|
schemeHTTP = "http"
|
||||||
|
schemeHTTPS = "https"
|
||||||
|
schemeH2C = "h2c"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Provider holds configurations of the provider.
|
// Provider holds configurations of the provider.
|
||||||
|
@ -357,7 +363,13 @@ func (p *Provider) loadConfigurationFromGateways(ctx context.Context) *dynamic.C
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gateways := p.client.ListGateways()
|
var gateways []*gatev1.Gateway
|
||||||
|
for _, gateway := range p.client.ListGateways() {
|
||||||
|
if _, ok := gatewayClassNames[string(gateway.Spec.GatewayClassName)]; !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
gateways = append(gateways, gateway)
|
||||||
|
}
|
||||||
|
|
||||||
var gatewayListeners []gatewayListener
|
var gatewayListeners []gatewayListener
|
||||||
for _, gateway := range gateways {
|
for _, gateway := range gateways {
|
||||||
|
@ -366,10 +378,6 @@ func (p *Provider) loadConfigurationFromGateways(ctx context.Context) *dynamic.C
|
||||||
Str("namespace", gateway.Namespace).
|
Str("namespace", gateway.Namespace).
|
||||||
Logger()
|
Logger()
|
||||||
|
|
||||||
if _, ok := gatewayClassNames[string(gateway.Spec.GatewayClassName)]; !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
gatewayListeners = append(gatewayListeners, p.loadGatewayListeners(logger.WithContext(ctx), gateway, conf)...)
|
gatewayListeners = append(gatewayListeners, p.loadGatewayListeners(logger.WithContext(ctx), gateway, conf)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1117,24 +1125,36 @@ func allowRoute(listener gatewayListener, routeNamespace, routeKind string) bool
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchListener(listener gatewayListener, routeNamespace string, parentRef gatev1.ParentReference) bool {
|
func matchingGatewayListeners(gatewayListeners []gatewayListener, routeNamespace string, parentRefs []gatev1.ParentReference) []gatewayListener {
|
||||||
|
var listeners []gatewayListener
|
||||||
|
|
||||||
|
for _, listener := range gatewayListeners {
|
||||||
|
for _, parentRef := range parentRefs {
|
||||||
if ptr.Deref(parentRef.Group, gatev1.GroupName) != gatev1.GroupName {
|
if ptr.Deref(parentRef.Group, gatev1.GroupName) != gatev1.GroupName {
|
||||||
return false
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if ptr.Deref(parentRef.Kind, kindGateway) != kindGateway {
|
if ptr.Deref(parentRef.Kind, kindGateway) != kindGateway {
|
||||||
return false
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
parentRefNamespace := string(ptr.Deref(parentRef.Namespace, gatev1.Namespace(routeNamespace)))
|
parentRefNamespace := string(ptr.Deref(parentRef.Namespace, gatev1.Namespace(routeNamespace)))
|
||||||
if listener.GWNamespace != parentRefNamespace {
|
if listener.GWNamespace != parentRefNamespace {
|
||||||
return false
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if string(parentRef.Name) != listener.GWName {
|
if string(parentRef.Name) != listener.GWName {
|
||||||
return false
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
listeners = append(listeners, listener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return listeners
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchListener(listener gatewayListener, parentRef gatev1.ParentReference) bool {
|
||||||
sectionName := string(ptr.Deref(parentRef.SectionName, ""))
|
sectionName := string(ptr.Deref(parentRef.SectionName, ""))
|
||||||
if sectionName != "" && sectionName != listener.Name {
|
if sectionName != "" && sectionName != listener.Name {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -49,6 +49,47 @@ func init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGatewayClassLabelSelector(t *testing.T) {
|
||||||
|
k8sObjects, gwObjects := readResources(t, []string{"gatewayclass_labelselector.yaml"})
|
||||||
|
|
||||||
|
kubeClient := kubefake.NewSimpleClientset(k8sObjects...)
|
||||||
|
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
|
||||||
|
|
||||||
|
client := newClientImpl(kubeClient, gwClient)
|
||||||
|
|
||||||
|
// This is initialized by the Provider init method but this cannot be called in a unit test.
|
||||||
|
client.labelSelector = "name=traefik-internal"
|
||||||
|
|
||||||
|
eventCh, err := client.WatchAll(nil, make(chan struct{}))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
|
||||||
|
// just wait for the first event
|
||||||
|
<-eventCh
|
||||||
|
}
|
||||||
|
|
||||||
|
p := Provider{
|
||||||
|
EntryPoints: map[string]Entrypoint{"http": {Address: ":9080"}},
|
||||||
|
StatusAddress: &StatusAddress{IP: "1.2.3.4"},
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = p.loadConfigurationFromGateways(context.Background())
|
||||||
|
|
||||||
|
gw, err := gwClient.GatewayV1().Gateways("default").Get(context.Background(), "traefik-external", metav1.GetOptions{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Empty(t, gw.Status.Addresses)
|
||||||
|
|
||||||
|
gw, err = gwClient.GatewayV1().Gateways("default").Get(context.Background(), "traefik-internal", metav1.GetOptions{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, gw.Status.Addresses, 1)
|
||||||
|
require.NotNil(t, gw.Status.Addresses[0].Type)
|
||||||
|
|
||||||
|
assert.Equal(t, gatev1.IPAddressType, *gw.Status.Addresses[0].Type)
|
||||||
|
assert.Equal(t, "1.2.3.4", gw.Status.Addresses[0].Value)
|
||||||
|
}
|
||||||
|
|
||||||
func TestLoadHTTPRoutes(t *testing.T) {
|
func TestLoadHTTPRoutes(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
@ -6738,127 +6779,171 @@ func TestLoadRoutesWithReferenceGrants(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_matchingGatewayListener(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
gwListeners []gatewayListener
|
||||||
|
parentRefs []gatev1.ParentReference
|
||||||
|
routeNamespace string
|
||||||
|
wantLen int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "Unsupported group",
|
||||||
|
gwListeners: []gatewayListener{{
|
||||||
|
Name: "foo",
|
||||||
|
GWName: "gateway",
|
||||||
|
GWNamespace: "default",
|
||||||
|
}},
|
||||||
|
parentRefs: []gatev1.ParentReference{{
|
||||||
|
Group: ptr.To(gatev1.Group("foo")),
|
||||||
|
}},
|
||||||
|
wantLen: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Unsupported kind",
|
||||||
|
gwListeners: []gatewayListener{{
|
||||||
|
Name: "foo",
|
||||||
|
GWName: "gateway",
|
||||||
|
GWNamespace: "default",
|
||||||
|
}},
|
||||||
|
parentRefs: []gatev1.ParentReference{{
|
||||||
|
Group: ptr.To(gatev1.Group(gatev1.GroupName)),
|
||||||
|
Kind: ptr.To(gatev1.Kind("foo")),
|
||||||
|
}},
|
||||||
|
wantLen: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Namespace does not match the listener",
|
||||||
|
gwListeners: []gatewayListener{{
|
||||||
|
Name: "foo",
|
||||||
|
GWName: "gateway",
|
||||||
|
GWNamespace: "default",
|
||||||
|
}},
|
||||||
|
parentRefs: []gatev1.ParentReference{{
|
||||||
|
Namespace: ptr.To(gatev1.Namespace("foo")),
|
||||||
|
Group: ptr.To(gatev1.Group(gatev1.GroupName)),
|
||||||
|
Kind: ptr.To(gatev1.Kind("Gateway")),
|
||||||
|
}},
|
||||||
|
wantLen: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Route namespace defaulting does not match the listener",
|
||||||
|
gwListeners: []gatewayListener{{
|
||||||
|
Name: "foo",
|
||||||
|
GWName: "gateway",
|
||||||
|
GWNamespace: "default",
|
||||||
|
}},
|
||||||
|
routeNamespace: "foo",
|
||||||
|
parentRefs: []gatev1.ParentReference{{
|
||||||
|
Group: ptr.To(gatev1.Group(gatev1.GroupName)),
|
||||||
|
Kind: ptr.To(gatev1.Kind("Gateway")),
|
||||||
|
}},
|
||||||
|
wantLen: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Name does not match the listener",
|
||||||
|
gwListeners: []gatewayListener{{
|
||||||
|
GWName: "gateway",
|
||||||
|
GWNamespace: "default",
|
||||||
|
}},
|
||||||
|
parentRefs: []gatev1.ParentReference{{
|
||||||
|
Namespace: ptr.To(gatev1.Namespace("default")),
|
||||||
|
Name: "foo",
|
||||||
|
Group: ptr.To(gatev1.Group(gatev1.GroupName)),
|
||||||
|
Kind: ptr.To(gatev1.Kind("Gateway")),
|
||||||
|
}},
|
||||||
|
wantLen: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Match",
|
||||||
|
gwListeners: []gatewayListener{{
|
||||||
|
GWName: "gateway",
|
||||||
|
GWNamespace: "default",
|
||||||
|
}},
|
||||||
|
parentRefs: []gatev1.ParentReference{{
|
||||||
|
Name: "gateway",
|
||||||
|
Namespace: ptr.To(gatev1.Namespace("default")),
|
||||||
|
Group: ptr.To(gatev1.Group(gatev1.GroupName)),
|
||||||
|
Kind: ptr.To(gatev1.Kind("Gateway")),
|
||||||
|
}},
|
||||||
|
wantLen: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Match with route namespace defaulting",
|
||||||
|
gwListeners: []gatewayListener{{
|
||||||
|
GWName: "gateway",
|
||||||
|
GWNamespace: "default",
|
||||||
|
}},
|
||||||
|
routeNamespace: "default",
|
||||||
|
parentRefs: []gatev1.ParentReference{{
|
||||||
|
Name: "gateway",
|
||||||
|
Group: ptr.To(gatev1.Group(gatev1.GroupName)),
|
||||||
|
Kind: ptr.To(gatev1.Kind("Gateway")),
|
||||||
|
}},
|
||||||
|
wantLen: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
listeners := matchingGatewayListeners(test.gwListeners, test.routeNamespace, test.parentRefs)
|
||||||
|
assert.Len(t, listeners, test.wantLen)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Test_matchListener(t *testing.T) {
|
func Test_matchListener(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
gwListener gatewayListener
|
gwListener gatewayListener
|
||||||
parentRef gatev1.ParentReference
|
parentRef gatev1.ParentReference
|
||||||
routeNamespace string
|
|
||||||
wantMatch bool
|
wantMatch bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "Unsupported group",
|
desc: "Section do not match",
|
||||||
gwListener: gatewayListener{
|
gwListener: gatewayListener{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
GWName: "gateway",
|
Port: gatev1.PortNumber(80),
|
||||||
GWNamespace: "default",
|
|
||||||
},
|
|
||||||
parentRef: gatev1.ParentReference{
|
|
||||||
Group: ptr.To(gatev1.Group("foo")),
|
|
||||||
},
|
|
||||||
wantMatch: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "Unsupported kind",
|
|
||||||
gwListener: gatewayListener{
|
|
||||||
Name: "foo",
|
|
||||||
GWName: "gateway",
|
|
||||||
GWNamespace: "default",
|
|
||||||
},
|
|
||||||
parentRef: gatev1.ParentReference{
|
|
||||||
Group: ptr.To(gatev1.Group(gatev1.GroupName)),
|
|
||||||
Kind: ptr.To(gatev1.Kind("foo")),
|
|
||||||
},
|
|
||||||
wantMatch: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "Namespace does not match the listener",
|
|
||||||
gwListener: gatewayListener{
|
|
||||||
Name: "foo",
|
|
||||||
GWName: "gateway",
|
|
||||||
GWNamespace: "default",
|
|
||||||
},
|
|
||||||
parentRef: gatev1.ParentReference{
|
|
||||||
Namespace: ptr.To(gatev1.Namespace("foo")),
|
|
||||||
Group: ptr.To(gatev1.Group(gatev1.GroupName)),
|
|
||||||
Kind: ptr.To(gatev1.Kind("Gateway")),
|
|
||||||
},
|
|
||||||
wantMatch: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "Route namespace defaulting does not match the listener",
|
|
||||||
gwListener: gatewayListener{
|
|
||||||
Name: "foo",
|
|
||||||
GWName: "gateway",
|
|
||||||
GWNamespace: "default",
|
|
||||||
},
|
|
||||||
routeNamespace: "foo",
|
|
||||||
parentRef: gatev1.ParentReference{
|
|
||||||
Group: ptr.To(gatev1.Group(gatev1.GroupName)),
|
|
||||||
Kind: ptr.To(gatev1.Kind("Gateway")),
|
|
||||||
},
|
|
||||||
wantMatch: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "Name does not match the listener",
|
|
||||||
gwListener: gatewayListener{
|
|
||||||
Name: "foo",
|
|
||||||
GWName: "gateway",
|
|
||||||
GWNamespace: "default",
|
|
||||||
},
|
|
||||||
parentRef: gatev1.ParentReference{
|
|
||||||
Namespace: ptr.To(gatev1.Namespace("default")),
|
|
||||||
Name: "foo",
|
|
||||||
Group: ptr.To(gatev1.Group(gatev1.GroupName)),
|
|
||||||
Kind: ptr.To(gatev1.Kind("Gateway")),
|
|
||||||
},
|
|
||||||
wantMatch: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "SectionName does not match a listener",
|
|
||||||
gwListener: gatewayListener{
|
|
||||||
Name: "foo",
|
|
||||||
GWName: "gateway",
|
|
||||||
GWNamespace: "default",
|
|
||||||
},
|
},
|
||||||
parentRef: gatev1.ParentReference{
|
parentRef: gatev1.ParentReference{
|
||||||
SectionName: ptr.To(gatev1.SectionName("bar")),
|
SectionName: ptr.To(gatev1.SectionName("bar")),
|
||||||
Name: "gateway",
|
Port: ptr.To(gatev1.PortNumber(80)),
|
||||||
Namespace: ptr.To(gatev1.Namespace("default")),
|
|
||||||
Group: ptr.To(gatev1.Group(gatev1.GroupName)),
|
|
||||||
Kind: ptr.To(gatev1.Kind("Gateway")),
|
|
||||||
},
|
},
|
||||||
wantMatch: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Match",
|
desc: "Section matches",
|
||||||
gwListener: gatewayListener{
|
gwListener: gatewayListener{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
GWName: "gateway",
|
Port: gatev1.PortNumber(80),
|
||||||
GWNamespace: "default",
|
|
||||||
},
|
},
|
||||||
parentRef: gatev1.ParentReference{
|
parentRef: gatev1.ParentReference{
|
||||||
SectionName: ptr.To(gatev1.SectionName("foo")),
|
SectionName: ptr.To(gatev1.SectionName("foo")),
|
||||||
Name: "gateway",
|
Port: ptr.To(gatev1.PortNumber(80)),
|
||||||
Namespace: ptr.To(gatev1.Namespace("default")),
|
|
||||||
Group: ptr.To(gatev1.Group(gatev1.GroupName)),
|
|
||||||
Kind: ptr.To(gatev1.Kind("Gateway")),
|
|
||||||
},
|
},
|
||||||
wantMatch: true,
|
wantMatch: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Match with route namespace defaulting",
|
desc: "Port do not match",
|
||||||
gwListener: gatewayListener{
|
gwListener: gatewayListener{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
GWName: "gateway",
|
Port: gatev1.PortNumber(90),
|
||||||
GWNamespace: "default",
|
|
||||||
},
|
},
|
||||||
routeNamespace: "default",
|
|
||||||
parentRef: gatev1.ParentReference{
|
parentRef: gatev1.ParentReference{
|
||||||
SectionName: ptr.To(gatev1.SectionName("foo")),
|
SectionName: ptr.To(gatev1.SectionName("foo")),
|
||||||
Name: "gateway",
|
Port: ptr.To(gatev1.PortNumber(80)),
|
||||||
Group: ptr.To(gatev1.Group(gatev1.GroupName)),
|
},
|
||||||
Kind: ptr.To(gatev1.Kind("Gateway")),
|
},
|
||||||
|
{
|
||||||
|
desc: "Port matches",
|
||||||
|
gwListener: gatewayListener{
|
||||||
|
Name: "foo",
|
||||||
|
Port: gatev1.PortNumber(80),
|
||||||
|
},
|
||||||
|
parentRef: gatev1.ParentReference{
|
||||||
|
SectionName: ptr.To(gatev1.SectionName("foo")),
|
||||||
|
Port: ptr.To(gatev1.PortNumber(80)),
|
||||||
},
|
},
|
||||||
wantMatch: true,
|
wantMatch: true,
|
||||||
},
|
},
|
||||||
|
@ -6868,7 +6953,7 @@ func Test_matchListener(t *testing.T) {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
gotMatch := matchListener(test.gwListener, test.routeNamespace, test.parentRef)
|
gotMatch := matchListener(test.gwListener, test.parentRef)
|
||||||
assert.Equal(t, test.wantMatch, gotMatch)
|
assert.Equal(t, test.wantMatch, gotMatch)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,11 @@ func (p *Provider) loadTCPRoutes(ctx context.Context, gatewayListeners []gateway
|
||||||
Str("namespace", route.Namespace).
|
Str("namespace", route.Namespace).
|
||||||
Logger()
|
Logger()
|
||||||
|
|
||||||
|
routeListeners := matchingGatewayListeners(gatewayListeners, route.Namespace, route.Spec.ParentRefs)
|
||||||
|
if len(routeListeners) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
var parentStatuses []gatev1alpha2.RouteParentStatus
|
var parentStatuses []gatev1alpha2.RouteParentStatus
|
||||||
for _, parentRef := range route.Spec.ParentRefs {
|
for _, parentRef := range route.Spec.ParentRefs {
|
||||||
parentStatus := &gatev1alpha2.RouteParentStatus{
|
parentStatus := &gatev1alpha2.RouteParentStatus{
|
||||||
|
@ -48,11 +53,9 @@ func (p *Provider) loadTCPRoutes(ctx context.Context, gatewayListeners []gateway
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, listener := range gatewayListeners {
|
for _, listener := range routeListeners {
|
||||||
accepted := true
|
accepted := matchListener(listener, parentRef)
|
||||||
if !matchListener(listener, route.Namespace, parentRef) {
|
|
||||||
accepted = false
|
|
||||||
}
|
|
||||||
if accepted && !allowRoute(listener, route.Namespace, kindTCPRoute) {
|
if accepted && !allowRoute(listener, route.Namespace, kindTCPRoute) {
|
||||||
parentStatus.Conditions = updateRouteConditionAccepted(parentStatus.Conditions, string(gatev1.RouteReasonNotAllowedByListeners))
|
parentStatus.Conditions = updateRouteConditionAccepted(parentStatus.Conditions, string(gatev1.RouteReasonNotAllowedByListeners))
|
||||||
accepted = false
|
accepted = false
|
||||||
|
|
|
@ -32,6 +32,11 @@ func (p *Provider) loadTLSRoutes(ctx context.Context, gatewayListeners []gateway
|
||||||
Str("tls_route", route.Name).
|
Str("tls_route", route.Name).
|
||||||
Str("namespace", route.Namespace).Logger()
|
Str("namespace", route.Namespace).Logger()
|
||||||
|
|
||||||
|
routeListeners := matchingGatewayListeners(gatewayListeners, route.Namespace, route.Spec.ParentRefs)
|
||||||
|
if len(routeListeners) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
var parentStatuses []gatev1alpha2.RouteParentStatus
|
var parentStatuses []gatev1alpha2.RouteParentStatus
|
||||||
for _, parentRef := range route.Spec.ParentRefs {
|
for _, parentRef := range route.Spec.ParentRefs {
|
||||||
parentStatus := &gatev1alpha2.RouteParentStatus{
|
parentStatus := &gatev1alpha2.RouteParentStatus{
|
||||||
|
@ -48,11 +53,9 @@ func (p *Provider) loadTLSRoutes(ctx context.Context, gatewayListeners []gateway
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, listener := range gatewayListeners {
|
for _, listener := range routeListeners {
|
||||||
accepted := true
|
accepted := matchListener(listener, parentRef)
|
||||||
if !matchListener(listener, route.Namespace, parentRef) {
|
|
||||||
accepted = false
|
|
||||||
}
|
|
||||||
if accepted && !allowRoute(listener, route.Namespace, kindTLSRoute) {
|
if accepted && !allowRoute(listener, route.Namespace, kindTLSRoute) {
|
||||||
parentStatus.Conditions = updateRouteConditionAccepted(parentStatus.Conditions, string(gatev1.RouteReasonNotAllowedByListeners))
|
parentStatus.Conditions = updateRouteConditionAccepted(parentStatus.Conditions, string(gatev1.RouteReasonNotAllowedByListeners))
|
||||||
accepted = false
|
accepted = false
|
||||||
|
|
|
@ -117,6 +117,9 @@ func TestConnPool_MaxIdleConn(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGC(t *testing.T) {
|
func TestGC(t *testing.T) {
|
||||||
|
// TODO: make the test stable if possible.
|
||||||
|
t.Skip("This test is flaky")
|
||||||
|
|
||||||
var isDestroyed bool
|
var isDestroyed bool
|
||||||
pools := map[string]*connPool{}
|
pools := map[string]*connPool{}
|
||||||
dialer := func() (net.Conn, error) {
|
dialer := func() (net.Conn, error) {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# shellcheck disable=SC2046
|
|
||||||
|
|
||||||
set -e -o pipefail
|
set -e -o pipefail
|
||||||
|
|
||||||
|
# shellcheck disable=SC1091 # Cannot check source of this file
|
||||||
source /go/src/k8s.io/code-generator/kube_codegen.sh
|
source /go/src/k8s.io/code-generator/kube_codegen.sh
|
||||||
|
|
||||||
git config --global --add safe.directory "/go/src/${PROJECT_MODULE}"
|
git config --global --add safe.directory "/go/src/${PROJECT_MODULE}"
|
||||||
|
|
|
@ -4,11 +4,11 @@ RepositoryName = "traefik"
|
||||||
OutputType = "file"
|
OutputType = "file"
|
||||||
FileName = "traefik_changelog.md"
|
FileName = "traefik_changelog.md"
|
||||||
|
|
||||||
# example new bugfix v3.1.5
|
# example new bugfix v3.1.6
|
||||||
CurrentRef = "v3.1"
|
CurrentRef = "v3.1"
|
||||||
PreviousRef = "v3.1.4"
|
PreviousRef = "v3.1.5"
|
||||||
BaseBranch = "v3.1"
|
BaseBranch = "v3.1"
|
||||||
FutureCurrentRefName = "v3.1.5"
|
FutureCurrentRefName = "v3.1.6"
|
||||||
|
|
||||||
ThresholdPreviousRef = 10
|
ThresholdPreviousRef = 10
|
||||||
ThresholdCurrentRef = 10
|
ThresholdCurrentRef = 10
|
||||||
|
|
|
@ -10,8 +10,8 @@ PreviousRef = "v3.1.0-rc1"
|
||||||
BaseBranch = "master"
|
BaseBranch = "master"
|
||||||
FutureCurrentRefName = "v3.2.0-rc1"
|
FutureCurrentRefName = "v3.2.0-rc1"
|
||||||
|
|
||||||
ThresholdPreviousRef = 10000
|
ThresholdPreviousRef = 10
|
||||||
ThresholdCurrentRef = 10000
|
ThresholdCurrentRef = 10
|
||||||
|
|
||||||
Debug = true
|
Debug = true
|
||||||
DisplayLabel = true
|
DisplayLabel = true
|
||||||
|
|
|
@ -4,11 +4,11 @@ RepositoryName = "traefik"
|
||||||
OutputType = "file"
|
OutputType = "file"
|
||||||
FileName = "traefik_changelog.md"
|
FileName = "traefik_changelog.md"
|
||||||
|
|
||||||
# example rc3 of v3.1.0
|
# example rc2 of v3.2.0
|
||||||
CurrentRef = "v3.1"
|
CurrentRef = "v3.2"
|
||||||
PreviousRef = "v3.1.0-rc2"
|
PreviousRef = "v3.2.0-rc1"
|
||||||
BaseBranch = "v3.1"
|
BaseBranch = "v3.2"
|
||||||
FutureCurrentRefName = "v3.1.0-rc3"
|
FutureCurrentRefName = "v3.2.0-rc2"
|
||||||
|
|
||||||
ThresholdPreviousRef = 10
|
ThresholdPreviousRef = 10
|
||||||
ThresholdCurrentRef = 10
|
ThresholdCurrentRef = 10
|
||||||
|
|
|
@ -15,9 +15,9 @@ for os in linux darwin windows freebsd openbsd; do
|
||||||
go clean -cache
|
go clean -cache
|
||||||
done
|
done
|
||||||
|
|
||||||
cat dist/**/*_checksums.txt >> dist/traefik_${VERSION}_checksums.txt
|
cat dist/**/*_checksums.txt >> "dist/traefik_${VERSION}_checksums.txt"
|
||||||
rm dist/**/*_checksums.txt
|
rm dist/**/*_checksums.txt
|
||||||
tar cfz dist/traefik-${VERSION}.src.tar.gz \
|
tar cfz "dist/traefik-${VERSION}.src.tar.gz" \
|
||||||
--exclude-vcs \
|
--exclude-vcs \
|
||||||
--exclude .idea \
|
--exclude .idea \
|
||||||
--exclude .travis \
|
--exclude .travis \
|
||||||
|
@ -25,4 +25,4 @@ tar cfz dist/traefik-${VERSION}.src.tar.gz \
|
||||||
--exclude .github \
|
--exclude .github \
|
||||||
--exclude dist .
|
--exclude dist .
|
||||||
|
|
||||||
chown -R $(id -u):$(id -g) dist/
|
chown -R "$(id -u)":"$(id -g)" dist/
|
||||||
|
|
|
@ -6,6 +6,7 @@ script_dir="$( cd "$( dirname "${0}" )" && pwd -P)"
|
||||||
|
|
||||||
if command -v shellcheck
|
if command -v shellcheck
|
||||||
then
|
then
|
||||||
|
exit_code=0
|
||||||
# The list of shell script come from the (grep ...) command, feeding the loop
|
# The list of shell script come from the (grep ...) command, feeding the loop
|
||||||
while IFS= read -r script_to_check
|
while IFS= read -r script_to_check
|
||||||
do
|
do
|
||||||
|
@ -18,7 +19,13 @@ then
|
||||||
| grep -v '.git/' | grep -v 'vendor/' | grep -v 'node_modules/' \
|
| grep -v '.git/' | grep -v 'vendor/' | grep -v 'node_modules/' \
|
||||||
| cut -d':' -f1
|
| cut -d':' -f1
|
||||||
)
|
)
|
||||||
wait # Wait for all background command to be completed
|
# Wait for all background command to be completed
|
||||||
|
for p in $(jobs -p)
|
||||||
|
do
|
||||||
|
wait "$p" || exit_code=$?
|
||||||
|
done
|
||||||
|
exit $exit_code
|
||||||
else
|
else
|
||||||
echo "== Command shellcheck not found in your PATH. No shell script checked."
|
echo "== Command shellcheck not found in your PATH. No shell script checked."
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
FROM node:20.14
|
FROM node:22.9-alpine3.20
|
||||||
# Current Active LTS release according to (https://nodejs.org/en/about/releases/)
|
# Current Active LTS release according to (https://nodejs.org/en/about/releases/)
|
||||||
|
|
||||||
ENV WEBUI_DIR /src/webui
|
ENV WEBUI_DIR=/src/webui
|
||||||
RUN mkdir -p $WEBUI_DIR
|
RUN mkdir -p $WEBUI_DIR
|
||||||
|
|
||||||
COPY package.json $WEBUI_DIR/
|
COPY package.json $WEBUI_DIR/
|
||||||
|
|
|
@ -50,8 +50,11 @@
|
||||||
"postcss": "^8.4.14",
|
"postcss": "^8.4.14",
|
||||||
"vitest": "^1.6.0"
|
"vitest": "^1.6.0"
|
||||||
},
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"cookie": "^0.7.0"
|
||||||
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20 || ^18 || ^16",
|
"node": "^22 || ^20 || ^18 || ^16",
|
||||||
"npm": ">= 6.13.4",
|
"npm": ">= 6.13.4",
|
||||||
"yarn": ">= 1.22.22"
|
"yarn": ">= 1.22.22"
|
||||||
},
|
},
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<div
|
<div
|
||||||
v-for="(entryItems, index) in entryAll.items"
|
v-for="(entryItems, index) in entryAll.items"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="col-12 col-sm-6 col-md-2"
|
class="col-12 col-sm-6 col-md-3"
|
||||||
>
|
>
|
||||||
<panel-entry
|
<panel-entry
|
||||||
:name="entryItems.name"
|
:name="entryItems.name"
|
||||||
|
|
2799
webui/yarn.lock
2799
webui/yarn.lock
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue