Compare commits
No commits in common. "5fe469162558511f0cf942adba83efc638f398b3" and "e0f19dd5f0134a09cfea1073d48ea243f50d2c51" have entirely different histories.
5fe4691625
...
e0f19dd5f0
70 changed files with 2695 additions and 3961 deletions
|
@ -213,7 +213,6 @@ issues:
|
||||||
- 'Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked'
|
- 'Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked'
|
||||||
- "should have a package comment, unless it's in another file for this package"
|
- "should have a package comment, unless it's in another file for this package"
|
||||||
- 'fmt.Sprintf can be replaced with string'
|
- 'fmt.Sprintf can be replaced with string'
|
||||||
- 'SA1019: dockertypes.ContainerNode is deprecated'
|
|
||||||
exclude-rules:
|
exclude-rules:
|
||||||
- path: '(.+)_test.go'
|
- path: '(.+)_test.go'
|
||||||
linters:
|
linters:
|
||||||
|
|
30
CHANGELOG.md
30
CHANGELOG.md
|
@ -1,33 +1,3 @@
|
||||||
## [v3.1.2](https://github.com/traefik/traefik/tree/v3.1.2) (2024-08-06)
|
|
||||||
[All Commits](https://github.com/traefik/traefik/compare/v3.1.1...v3.1.2)
|
|
||||||
|
|
||||||
**Bug fixes:**
|
|
||||||
- **[k8s,k8s/gatewayapi]** Include status addresses when comparing Gateway statuses ([#10972](https://github.com/traefik/traefik/pull/10972) by [kevinpollet](https://github.com/kevinpollet))
|
|
||||||
- **[k8s/ingress,k8s/crd,k8s]** Allow to disable Kubernetes cluster scope resources discovery ([#10946](https://github.com/traefik/traefik/pull/10946) by [rtribotte](https://github.com/rtribotte))
|
|
||||||
- **[logs]** Change logs output from stderr to stdout ([#10973](https://github.com/traefik/traefik/pull/10973) by [rtribotte](https://github.com/rtribotte))
|
|
||||||
- Fix grafana dashboard to work with scrape interval greater than 15s ([#10954](https://github.com/traefik/traefik/pull/10954) by [swiffer](https://github.com/swiffer))
|
|
||||||
|
|
||||||
**Documentation:**
|
|
||||||
- **[accesslogs]** Add Access logs section to the migration guide ([#10947](https://github.com/traefik/traefik/pull/10947) by [lbenguigui](https://github.com/lbenguigui))
|
|
||||||
- **[http]** Fix missing codeblock ending in HTTP discover documentation ([#10967](https://github.com/traefik/traefik/pull/10967) by [djcode](https://github.com/djcode))
|
|
||||||
- **[http]** Fix yaml config example for HTTP provider headers ([#10966](https://github.com/traefik/traefik/pull/10966) by [djcode](https://github.com/djcode))
|
|
||||||
- **[k8s,k8s/gatewayapi]** Use Standard channel by default with Gateway API ([#10974](https://github.com/traefik/traefik/pull/10974) by [mloiseleur](https://github.com/mloiseleur))
|
|
||||||
|
|
||||||
**Misc:**
|
|
||||||
- Merge branch v2.11 into v3.1 ([#10978](https://github.com/traefik/traefik/pull/10978) by [rtribotte](https://github.com/rtribotte))
|
|
||||||
- Merge v2.11 into v3.1 ([#10956](https://github.com/traefik/traefik/pull/10956) by [mmatur](https://github.com/mmatur))
|
|
||||||
|
|
||||||
## [v2.11.8](https://github.com/traefik/traefik/tree/v2.11.8) (2024-08-06)
|
|
||||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.7...v2.11.8)
|
|
||||||
|
|
||||||
**Bug fixes:**
|
|
||||||
- **[docker]** Update to github.com/docker/docker v27.1.1 ([#10955](https://github.com/traefik/traefik/pull/10955) by [rtribotte](https://github.com/rtribotte))
|
|
||||||
- **[webui]** Upgrade webui dependencies ([#10961](https://github.com/traefik/traefik/pull/10961) by [mmatur](https://github.com/mmatur))
|
|
||||||
|
|
||||||
**Documentation:**
|
|
||||||
- Fix embedded youtube video ([#10958](https://github.com/traefik/traefik/pull/10958) by [mmatur](https://github.com/mmatur))
|
|
||||||
- Updated index.md to include video ([#10944](https://github.com/traefik/traefik/pull/10944) by [tomatokoolaid](https://github.com/tomatokoolaid))
|
|
||||||
|
|
||||||
## [v3.1.1](https://github.com/traefik/traefik/tree/v3.1.1) (2024-07-30)
|
## [v3.1.1](https://github.com/traefik/traefik/tree/v3.1.1) (2024-07-30)
|
||||||
[All Commits](https://github.com/traefik/traefik/compare/v3.1.0...v3.1.1)
|
[All Commits](https://github.com/traefik/traefik/compare/v3.1.0...v3.1.1)
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ func setupLogger(staticConfiguration *static.Configuration) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLogWriter(staticConfiguration *static.Configuration) io.Writer {
|
func getLogWriter(staticConfiguration *static.Configuration) io.Writer {
|
||||||
var w io.Writer = os.Stdout
|
var w io.Writer = os.Stderr
|
||||||
|
|
||||||
if staticConfiguration.Log != nil && len(staticConfiguration.Log.FilePath) > 0 {
|
if staticConfiguration.Log != nil && len(staticConfiguration.Log.FilePath) > 0 {
|
||||||
_, _ = os.OpenFile(staticConfiguration.Log.FilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o666)
|
_, _ = os.OpenFile(staticConfiguration.Log.FilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o666)
|
||||||
|
|
|
@ -1028,7 +1028,7 @@
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "5xx over $interval",
|
"title": "5xx over [$interval]",
|
||||||
"type": "timeseries"
|
"type": "timeseries"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1128,7 +1128,7 @@
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Other codes over $interval",
|
"title": "Other codes over [$interval]",
|
||||||
"type": "timeseries"
|
"type": "timeseries"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -242,7 +242,7 @@
|
||||||
"uid": "${DS_PROMETHEUS}"
|
"uid": "${DS_PROMETHEUS}"
|
||||||
},
|
},
|
||||||
"editorMode": "code",
|
"editorMode": "code",
|
||||||
"expr": "sum(rate(traefik_entrypoint_requests_total{entrypoint=~\"$entrypoint\"}[$interval])) by (entrypoint)",
|
"expr": "sum(rate(traefik_entrypoint_requests_total{entrypoint=~\"$entrypoint\"}[1m])) by (entrypoint)",
|
||||||
"legendFormat": "{{entrypoint}}",
|
"legendFormat": "{{entrypoint}}",
|
||||||
"range": true,
|
"range": true,
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
|
@ -340,7 +340,7 @@
|
||||||
"uid": "${DS_PROMETHEUS}"
|
"uid": "${DS_PROMETHEUS}"
|
||||||
},
|
},
|
||||||
"editorMode": "code",
|
"editorMode": "code",
|
||||||
"expr": "(sum(rate(traefik_entrypoint_request_duration_seconds_bucket{le=\"0.3\",code=\"200\",entrypoint=~\"$entrypoint\"}[$interval])) by (method) + \n sum(rate(traefik_entrypoint_request_duration_seconds_bucket{le=\"1.2\",code=\"200\",entrypoint=~\"$entrypoint\"}[$interval])) by (method)) / 2 / \n sum(rate(traefik_entrypoint_request_duration_seconds_count{code=\"200\",entrypoint=~\"$entrypoint\"}[$interval])) by (method)\n",
|
"expr": "(sum(rate(traefik_entrypoint_request_duration_seconds_bucket{le=\"0.3\",code=\"200\",entrypoint=~\"$entrypoint\"}[5m])) by (method) + \n sum(rate(traefik_entrypoint_request_duration_seconds_bucket{le=\"1.2\",code=\"200\",entrypoint=~\"$entrypoint\"}[5m])) by (method)) / 2 / \n sum(rate(traefik_entrypoint_request_duration_seconds_count{code=\"200\",entrypoint=~\"$entrypoint\"}[5m])) by (method)\n",
|
||||||
"legendFormat": "{{method}}",
|
"legendFormat": "{{method}}",
|
||||||
"range": true,
|
"range": true,
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
|
@ -408,7 +408,7 @@
|
||||||
"uid": "${DS_PROMETHEUS}"
|
"uid": "${DS_PROMETHEUS}"
|
||||||
},
|
},
|
||||||
"editorMode": "code",
|
"editorMode": "code",
|
||||||
"expr": "sum(rate(traefik_service_requests_total{service=~\"$service.*\",protocol=\"http\"}[$interval])) by (method, code)",
|
"expr": "sum(rate(traefik_service_requests_total{service=~\"$service.*\",protocol=\"http\"}[1m])) by (method, code)",
|
||||||
"legendFormat": "{{method}}[{{code}}]",
|
"legendFormat": "{{method}}[{{code}}]",
|
||||||
"range": true,
|
"range": true,
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
|
@ -606,7 +606,7 @@
|
||||||
"uid": "${DS_PROMETHEUS}"
|
"uid": "${DS_PROMETHEUS}"
|
||||||
},
|
},
|
||||||
"editorMode": "code",
|
"editorMode": "code",
|
||||||
"expr": "topk(15,\n label_replace(\n sum by (service,code) \n (rate(traefik_service_requests_total{service=~\"$service.*\",protocol=\"http\"}[$interval])) > 0,\n \"service\", \"$1\", \"service\", \"([^-]+-[^-]+).*\")\n)",
|
"expr": "topk(15,\n label_replace(\n sum by (service,code) \n (rate(traefik_service_requests_total{service=~\"$service.*\",protocol=\"http\"}[5m])) > 0,\n \"service\", \"$1\", \"service\", \"([^-]+-[^-]+).*\")\n)",
|
||||||
"legendFormat": "[{{code}}] on {{service}}",
|
"legendFormat": "[{{code}}] on {{service}}",
|
||||||
"range": true,
|
"range": true,
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
|
@ -710,7 +710,7 @@
|
||||||
"uid": "${DS_PROMETHEUS}"
|
"uid": "${DS_PROMETHEUS}"
|
||||||
},
|
},
|
||||||
"editorMode": "code",
|
"editorMode": "code",
|
||||||
"expr": "label_replace(\n 1 - (sum by (service)\n (rate(traefik_service_request_duration_seconds_bucket{le=\"1.2\",service=~\"$service.*\"}[$interval])) / sum by (service) \n (rate(traefik_service_request_duration_seconds_count{service=~\"$service.*\"}[$interval]))\n ) > 0,\n \"service\", \"$1\", \"service\", \"([^-]+-[^-]+).*\"\n)",
|
"expr": "label_replace(\n 1 - (sum by (service)\n (rate(traefik_service_request_duration_seconds_bucket{le=\"1.2\",service=~\"$service.*\"}[5m])) / sum by (service) \n (rate(traefik_service_request_duration_seconds_count{service=~\"$service.*\"}[5m]))\n ) > 0,\n \"service\", \"$1\", \"service\", \"([^-]+-[^-]+).*\"\n)",
|
||||||
"legendFormat": "{{service}}",
|
"legendFormat": "{{service}}",
|
||||||
"range": true,
|
"range": true,
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
|
@ -804,7 +804,7 @@
|
||||||
"uid": "${DS_PROMETHEUS}"
|
"uid": "${DS_PROMETHEUS}"
|
||||||
},
|
},
|
||||||
"editorMode": "code",
|
"editorMode": "code",
|
||||||
"expr": "label_replace(\n 1 - (sum by (service)\n (rate(traefik_service_request_duration_seconds_bucket{le=\"0.3\",service=~\"$service.*\"}[$interval])) / sum by (service) \n (rate(traefik_service_request_duration_seconds_count{service=~\"$service.*\"}[$interval]))\n ) > 0,\n \"service\", \"$1\", \"service\", \"([^-]+-[^-]+).*\"\n)",
|
"expr": "label_replace(\n 1 - (sum by (service)\n (rate(traefik_service_request_duration_seconds_bucket{le=\"0.3\",service=~\"$service.*\"}[5m])) / sum by (service) \n (rate(traefik_service_request_duration_seconds_count{service=~\"$service.*\"}[5m]))\n ) > 0,\n \"service\", \"$1\", \"service\", \"([^-]+-[^-]+).*\"\n)",
|
||||||
"legendFormat": "{{service}}",
|
"legendFormat": "{{service}}",
|
||||||
"range": true,
|
"range": true,
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
|
@ -916,13 +916,13 @@
|
||||||
"uid": "${DS_PROMETHEUS}"
|
"uid": "${DS_PROMETHEUS}"
|
||||||
},
|
},
|
||||||
"editorMode": "code",
|
"editorMode": "code",
|
||||||
"expr": "topk(15,\n label_replace(\n sum by (service,method,code) \n (rate(traefik_service_requests_total{service=~\"$service.*\",code=~\"2..\",protocol=\"http\"}[$interval])) > 0,\n \"service\", \"$1\", \"service\", \"([^-]+-[^-]+).*\")\n)",
|
"expr": "topk(15,\n label_replace(\n sum by (service,method,code) \n (rate(traefik_service_requests_total{service=~\"$service.*\",code=~\"2..\",protocol=\"http\"}[5m])) > 0,\n \"service\", \"$1\", \"service\", \"([^-]+-[^-]+).*\")\n)",
|
||||||
"legendFormat": "{{method}}[{{code}}] on {{service}}",
|
"legendFormat": "{{method}}[{{code}}] on {{service}}",
|
||||||
"range": true,
|
"range": true,
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "2xx over $interval",
|
"title": "2xx over 5 min",
|
||||||
"type": "timeseries"
|
"type": "timeseries"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1015,13 +1015,13 @@
|
||||||
"uid": "${DS_PROMETHEUS}"
|
"uid": "${DS_PROMETHEUS}"
|
||||||
},
|
},
|
||||||
"editorMode": "code",
|
"editorMode": "code",
|
||||||
"expr": "topk(15,\n label_replace(\n sum by (service,method,code) \n (rate(traefik_service_requests_total{service=~\"$service.*\",code=~\"5..\",protocol=\"http\"}[$interval])) > 0,\n \"service\", \"$1\", \"service\", \"([^-]+-[^-]+).*\")\n)",
|
"expr": "topk(15,\n label_replace(\n sum by (service,method,code) \n (rate(traefik_service_requests_total{service=~\"$service.*\",code=~\"5..\",protocol=\"http\"}[5m])) > 0,\n \"service\", \"$1\", \"service\", \"([^-]+-[^-]+).*\")\n)",
|
||||||
"legendFormat": "{{method}}[{{code}}] on {{service}}",
|
"legendFormat": "{{method}}[{{code}}] on {{service}}",
|
||||||
"range": true,
|
"range": true,
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "5xx over $interval",
|
"title": "5xx over 5 min",
|
||||||
"type": "timeseries"
|
"type": "timeseries"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1114,13 +1114,13 @@
|
||||||
"uid": "${DS_PROMETHEUS}"
|
"uid": "${DS_PROMETHEUS}"
|
||||||
},
|
},
|
||||||
"editorMode": "code",
|
"editorMode": "code",
|
||||||
"expr": "topk(15,\n label_replace(\n sum by (service,method,code) \n (rate(traefik_service_requests_total{service=~\"$service.*\",code!~\"2..|5..\",protocol=\"http\"}[$interval])) > 0,\n \"service\", \"$1\", \"service\", \"([^-]+-[^-]+).*\")\n)",
|
"expr": "topk(15,\n label_replace(\n sum by (service,method,code) \n (rate(traefik_service_requests_total{service=~\"$service.*\",code!~\"2..|5..\",protocol=\"http\"}[5m])) > 0,\n \"service\", \"$1\", \"service\", \"([^-]+-[^-]+).*\")\n)",
|
||||||
"legendFormat": "{{method}}[{{code}}] on {{service}}",
|
"legendFormat": "{{method}}[{{code}}] on {{service}}",
|
||||||
"range": true,
|
"range": true,
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Other codes over $interval",
|
"title": "Other codes over 5 min",
|
||||||
"type": "timeseries"
|
"type": "timeseries"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1213,7 +1213,7 @@
|
||||||
"uid": "${DS_PROMETHEUS}"
|
"uid": "${DS_PROMETHEUS}"
|
||||||
},
|
},
|
||||||
"editorMode": "code",
|
"editorMode": "code",
|
||||||
"expr": "topk(15,\n label_replace(\n sum by (service,method) \n (rate(traefik_service_requests_bytes_total{service=~\"$service.*\",protocol=\"http\"}[$interval])) > 0,\n \"service\", \"$1\", \"service\", \"([^-]+-[^-]+).*\")\n)",
|
"expr": "topk(15,\n label_replace(\n sum by (service,method) \n (rate(traefik_service_requests_bytes_total{service=~\"$service.*\",protocol=\"http\"}[1m])) > 0,\n \"service\", \"$1\", \"service\", \"([^-]+-[^-]+).*\")\n)",
|
||||||
"legendFormat": "{{method}} on {{service}}",
|
"legendFormat": "{{method}} on {{service}}",
|
||||||
"range": true,
|
"range": true,
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
|
@ -1312,7 +1312,7 @@
|
||||||
"uid": "${DS_PROMETHEUS}"
|
"uid": "${DS_PROMETHEUS}"
|
||||||
},
|
},
|
||||||
"editorMode": "code",
|
"editorMode": "code",
|
||||||
"expr": "topk(15,\n label_replace(\n sum by (service,method) \n (rate(traefik_service_responses_bytes_total{service=~\"$service.*\",protocol=\"http\"}[$interval])) > 0,\n \"service\", \"$1\", \"service\", \"([^-]+-[^-]+).*\")\n)",
|
"expr": "topk(15,\n label_replace(\n sum by (service,method) \n (rate(traefik_service_responses_bytes_total{service=~\"$service.*\",protocol=\"http\"}[1m])) > 0,\n \"service\", \"$1\", \"service\", \"([^-]+-[^-]+).*\")\n)",
|
||||||
"legendFormat": "{{method}} on {{service}}",
|
"legendFormat": "{{method}} on {{service}}",
|
||||||
"range": true,
|
"range": true,
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
|
@ -1448,69 +1448,6 @@
|
||||||
"skipUrlSync": false,
|
"skipUrlSync": false,
|
||||||
"type": "datasource"
|
"type": "datasource"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"auto": true,
|
|
||||||
"auto_count": 30,
|
|
||||||
"auto_min": "1m",
|
|
||||||
"current": {
|
|
||||||
"selected": false,
|
|
||||||
"text": "auto",
|
|
||||||
"value": "$__auto_interval_interval"
|
|
||||||
},
|
|
||||||
"hide": 0,
|
|
||||||
"name": "interval",
|
|
||||||
"options": [
|
|
||||||
{
|
|
||||||
"selected": true,
|
|
||||||
"text": "auto",
|
|
||||||
"value": "$__auto_interval_interval"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "1m",
|
|
||||||
"value": "1m"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "5m",
|
|
||||||
"value": "5m"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "10m",
|
|
||||||
"value": "10m"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "30m",
|
|
||||||
"value": "30m"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "1h",
|
|
||||||
"value": "1h"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "2h",
|
|
||||||
"value": "2h"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "4h",
|
|
||||||
"value": "4h"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "8h",
|
|
||||||
"value": "8h"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"query": "1m,5m,10m,30m,1h,2h,4h,8h",
|
|
||||||
"refresh": 2,
|
|
||||||
"skipUrlSync": false,
|
|
||||||
"type": "interval"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"current": {},
|
"current": {},
|
||||||
"datasource": {
|
"datasource": {
|
||||||
|
|
|
@ -617,7 +617,6 @@ It defaults to `2160` (90 days) to follow Let's Encrypt certificates' duration.
|
||||||
|----------------------|-------------------|-------------------------|
|
|----------------------|-------------------|-------------------------|
|
||||||
| >= 1 year | 4 months | 1 week |
|
| >= 1 year | 4 months | 1 week |
|
||||||
| >= 90 days | 30 days | 1 day |
|
| >= 90 days | 30 days | 1 day |
|
||||||
| >= 30 days | 10 days | 12 hours |
|
|
||||||
| >= 7 days | 1 day | 1 hour |
|
| >= 7 days | 1 day | 1 hour |
|
||||||
| >= 24 hours | 6 hours | 10 min |
|
| >= 24 hours | 6 hours | 10 min |
|
||||||
| < 24 hours | 20 min | 1 min |
|
| < 24 hours | 20 min | 1 min |
|
||||||
|
|
|
@ -7,23 +7,17 @@ description: "Traefik Proxy, an open source Edge Router, auto-discovers configur
|
||||||
|
|
||||||
![Architecture](assets/img/traefik-architecture.png)
|
![Architecture](assets/img/traefik-architecture.png)
|
||||||
|
|
||||||
Traefik is an [open-source](https://github.com/traefik/traefik) *Application Proxy* that makes publishing your services a fun and easy experience.
|
Traefik is an [open-source](https://github.com/traefik/traefik) *Edge Router* that makes publishing your services a fun and easy experience.
|
||||||
It receives requests on behalf of your system and identifies which components are responsible for handling them, and routes them securely.
|
It receives requests on behalf of your system and finds out which components are responsible for handling them.
|
||||||
|
|
||||||
What sets Traefik apart, besides its many features, is that it automatically discovers the right configuration for your services.
|
What sets Traefik apart, besides its many features, is that it automatically discovers the right configuration for your services.
|
||||||
The magic happens when Traefik inspects your infrastructure, where it finds relevant information and discovers which service serves which request.
|
The magic happens when Traefik inspects your infrastructure, where it finds relevant information and discovers which service serves which request.
|
||||||
|
|
||||||
Traefik is natively compliant with every major cluster technology, such as Kubernetes, Docker Swarm, AWS, and [the list goes on](providers/overview.md); and can handle many at the same time. (It even works for legacy software running on bare metal.)
|
Traefik is natively compliant with every major cluster technology, such as Kubernetes, Docker, Docker Swarm, AWS, and [the list goes on](providers/overview.md); and can handle many at the same time. (It even works for legacy software running on bare metal.)
|
||||||
|
|
||||||
With Traefik, there is no need to maintain and synchronize a separate configuration file: everything happens automatically, in real time (no restarts, no connection interruptions).
|
With Traefik, there is no need to maintain and synchronize a separate configuration file: everything happens automatically, in real time (no restarts, no connection interruptions).
|
||||||
With Traefik, you spend time developing and deploying new features to your system, not on configuring and maintaining its working state.
|
With Traefik, you spend time developing and deploying new features to your system, not on configuring and maintaining its working state.
|
||||||
|
|
||||||
And if your needs change, you can add API gateway and API management capabilities seamlessly to your existing Traefik deployments. It takes less than a minute, there’s no rip-and-replace, and all your configurations are preserved. See how it works in this video:
|
|
||||||
|
|
||||||
<div style="text-align: center;">
|
|
||||||
<iframe src="https://www.youtube.com/embed/zriUO5YPgFg?modestbranding=1&rel=0&controls=1" width="560" height="315" title="Upgrade Traefik Proxy to API Gateway and API Management in Seconds // Traefik Labs" frameborder="0" allowfullscreen></iframe>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
Developing Traefik, our main goal is to make it effortless to use, and we're sure you'll enjoy it.
|
Developing Traefik, our main goal is to make it effortless to use, and we're sure you'll enjoy it.
|
||||||
|
|
||||||
-- The Traefik Maintainer Team
|
-- The Traefik Maintainer Team
|
||||||
|
@ -32,4 +26,4 @@ Developing Traefik, our main goal is to make it effortless to use, and we're sur
|
||||||
|
|
||||||
Join our user friendly and active [Community Forum](https://community.traefik.io "Link to Traefik Community Forum") to discuss, learn, and connect with the Traefik community.
|
Join our user friendly and active [Community Forum](https://community.traefik.io "Link to Traefik Community Forum") to discuss, learn, and connect with the Traefik community.
|
||||||
|
|
||||||
Using Traefik OSS in Production? Consider our enterprise-grade [API Gateway](https://traefik.io/traefik-hub-api-gateway/), [API Management](https://traefik.io/traefik-hub/), and [Commercial Support](https://info.traefik.io/request-commercial-support) solutions.
|
Using Traefik OSS in Production? Add enterprise-grade API Gateway and API Management capabilities to your existing deployments seamlessly. No rip and replace. No learning curve. Learn more from [this short video](https://info.traefik.io/traefik-upgrade-walkthrough)
|
||||||
|
|
|
@ -255,48 +255,3 @@ http:
|
||||||
[http.middlewares.test-compress.compress]
|
[http.middlewares.test-compress.compress]
|
||||||
defaultEncoding = "gzip"
|
defaultEncoding = "gzip"
|
||||||
```
|
```
|
||||||
|
|
||||||
### `encodings`
|
|
||||||
|
|
||||||
_Optional, Default="zstd, br, gzip"_
|
|
||||||
|
|
||||||
`encodings` specifies the list of supported compression encodings.
|
|
||||||
At least one encoding value must be specified, and valid entries are `zstd` (Zstandard), `br` (Brotli), and `gzip` (Gzip).
|
|
||||||
The order of the list also sets the priority, the top entry has the highest priority.
|
|
||||||
|
|
||||||
```yaml tab="Docker & Swarm"
|
|
||||||
labels:
|
|
||||||
- "traefik.http.middlewares.test-compress.compress.encodings=zstd,br"
|
|
||||||
```
|
|
||||||
|
|
||||||
```yaml tab="Kubernetes"
|
|
||||||
apiVersion: traefik.io/v1alpha1
|
|
||||||
kind: Middleware
|
|
||||||
metadata:
|
|
||||||
name: test-compress
|
|
||||||
spec:
|
|
||||||
compress:
|
|
||||||
encodings:
|
|
||||||
- zstd
|
|
||||||
- br
|
|
||||||
```
|
|
||||||
|
|
||||||
```yaml tab="Consul Catalog"
|
|
||||||
- "traefik.http.middlewares.test-compress.compress.encodings=zstd,br"
|
|
||||||
```
|
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
|
||||||
http:
|
|
||||||
middlewares:
|
|
||||||
test-compress:
|
|
||||||
compress:
|
|
||||||
encodings:
|
|
||||||
- zstd
|
|
||||||
- br
|
|
||||||
```
|
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
|
||||||
[http.middlewares]
|
|
||||||
[http.middlewares.test-compress.compress]
|
|
||||||
encodings = ["zstd","br"]
|
|
||||||
```
|
|
||||||
|
|
|
@ -591,11 +591,6 @@ Please take a look at the observability documentation for more information:
|
||||||
- [Metrics](../observability/metrics/overview.md#addinternals)
|
- [Metrics](../observability/metrics/overview.md#addinternals)
|
||||||
- [Tracing](../observability/tracing/overview.md#addinternals)
|
- [Tracing](../observability/tracing/overview.md#addinternals)
|
||||||
|
|
||||||
#### Access logs
|
|
||||||
|
|
||||||
In v3, the `ServiceURL` field is not an object anymore but a string representation.
|
|
||||||
An update may be required if you index access logs.
|
|
||||||
|
|
||||||
## Dynamic Configuration Changes
|
## Dynamic Configuration Changes
|
||||||
|
|
||||||
### Router Rule Matchers
|
### Router Rule Matchers
|
||||||
|
|
|
@ -455,7 +455,7 @@ To enable HTTP/3 on an EntryPoint, please check out the [HTTP/3 configuration](.
|
||||||
|
|
||||||
In `v2.6`, the [Kubernetes Gateway API provider](../providers/kubernetes-gateway.md) now only supports the version [v1alpha2](https://gateway-api.sigs.k8s.io/v1alpha2/guides/) of the specification and
|
In `v2.6`, the [Kubernetes Gateway API provider](../providers/kubernetes-gateway.md) now only supports the version [v1alpha2](https://gateway-api.sigs.k8s.io/v1alpha2/guides/) of the specification and
|
||||||
[route namespaces](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.RouteNamespaces) selectors, which requires Traefik to fetch and watch the cluster namespaces.
|
[route namespaces](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.RouteNamespaces) selectors, which requires Traefik to fetch and watch the cluster namespaces.
|
||||||
Therefore, the RBAC and CRD definitions must be updated.
|
Therefore, the [RBAC](../reference/dynamic-configuration/kubernetes-gateway.md#rbac) and [CRD](../reference/dynamic-configuration/kubernetes-gateway.md#definitions) definitions must be updated.
|
||||||
|
|
||||||
## v2.6.0 to v2.6.1
|
## v2.6.0 to v2.6.1
|
||||||
|
|
||||||
|
|
|
@ -10,11 +10,9 @@ description: "Learn the steps needed to migrate to new Traefik Proxy v3 versions
|
||||||
### Kubernetes Provider RBACs
|
### Kubernetes Provider RBACs
|
||||||
|
|
||||||
Starting with v3.1, the Kubernetes Providers now use the [EndpointSlices API](https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/) (Kubernetes >=v1.21) to discover service endpoint addresses.
|
Starting with v3.1, the Kubernetes Providers now use the [EndpointSlices API](https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/) (Kubernetes >=v1.21) to discover service endpoint addresses.
|
||||||
It also brings NodePort load-balancing which requires Nodes resources lookup.
|
|
||||||
|
|
||||||
Therefore, in the corresponding RBACs (see [KubernetesIngress](../routing/providers/kubernetes-ingress.md#configuration-example), [KubernetesCRD](../reference/dynamic-configuration/kubernetes-crd.md#rbac), and [KubernetesGateway](../reference/dynamic-configuration/kubernetes-gateway-rbac.yml) provider RBACs):
|
Therefore, in the corresponding RBACs (see [KubernetesIngress](../routing/providers/kubernetes-ingress.md#configuration-example), [KubernetesCRD](../reference/dynamic-configuration/kubernetes-crd.md#rbac), and [KubernetesGateway](../reference/dynamic-configuration/kubernetes-gateway.md#rbac) provider RBACs),
|
||||||
|
the `endpoints` right has to be removed and the following `endpointslices` right has to be added.
|
||||||
- the `endpoints` right has to be removed and the following `endpointslices` right has to be added:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
...
|
...
|
||||||
|
@ -28,21 +26,6 @@ Therefore, in the corresponding RBACs (see [KubernetesIngress](../routing/provid
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
- the `nodes` right has to be added:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
...
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- nodes
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- list
|
|
||||||
- watch
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Gateway API: KubernetesGateway Provider
|
#### Gateway API: KubernetesGateway Provider
|
||||||
|
|
||||||
In v3.1, the KubernetesGateway Provider is no longer an experimental feature.
|
In v3.1, the KubernetesGateway Provider is no longer an experimental feature.
|
||||||
|
@ -68,10 +51,3 @@ It can be enabled without the associated `experimental.kubernetesgateway` option
|
||||||
|
|
||||||
The `kubernetesgateway` option should be removed from the experimental section of the static configuration.
|
The `kubernetesgateway` option should be removed from the experimental section of the static configuration.
|
||||||
To configure `kubernetesgateway`, please check out the [KubernetesGateway Provider documentation](../providers/kubernetes-gateway.md).
|
To configure `kubernetesgateway`, please check out the [KubernetesGateway Provider documentation](../providers/kubernetes-gateway.md).
|
||||||
|
|
||||||
## v3.1.0 to v3.1.1
|
|
||||||
|
|
||||||
### IngressClass Lookup
|
|
||||||
|
|
||||||
The Kubernetes Ingress provider option `disableIngressClassLookup` has been deprecated in v3.1.1, and will be removed in the next major version.
|
|
||||||
Please use the `disableClusterScopeResources` option instead to avoid cluster scope resources discovery (IngressClass, Nodes).
|
|
||||||
|
|
|
@ -134,7 +134,6 @@ You can specify which Docker API Endpoint to use with the directive [`endpoint`]
|
||||||
- Accounting at container level, by exposing the socket on a another container than Traefik's.
|
- Accounting at container level, by exposing the socket on a another container than Traefik's.
|
||||||
- Accounting at kernel level, by enforcing kernel calls with mechanisms like [SELinux](https://en.wikipedia.org/wiki/Security-Enhanced_Linux), to only allows an identified set of actions for Traefik's process (or the "socket exposer" process).
|
- Accounting at kernel level, by enforcing kernel calls with mechanisms like [SELinux](https://en.wikipedia.org/wiki/Security-Enhanced_Linux), to only allows an identified set of actions for Traefik's process (or the "socket exposer" process).
|
||||||
- SSH public key authentication (SSH is supported with Docker > 18.09)
|
- SSH public key authentication (SSH is supported with Docker > 18.09)
|
||||||
- Authentication using HTTP Basic authentication through an HTTP proxy that exposes the Docker daemon socket.
|
|
||||||
|
|
||||||
??? info "More Resources and Examples"
|
??? info "More Resources and Examples"
|
||||||
|
|
||||||
|
@ -217,50 +216,6 @@ See the [Docker API Access](#docker-api-access) section for more information.
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
??? example "Using HTTP"
|
|
||||||
|
|
||||||
Using Docker Engine API you can connect Traefik to remote daemon using HTTP.
|
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
|
||||||
providers:
|
|
||||||
docker:
|
|
||||||
endpoint: "http://127.0.0.1:2375"
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
|
||||||
[providers.docker]
|
|
||||||
endpoint = "http://127.0.0.1:2375"
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab="CLI"
|
|
||||||
--providers.docker.endpoint=http://127.0.0.1:2375
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
??? example "Using TCP"
|
|
||||||
|
|
||||||
Using Docker Engine API you can connect Traefik to remote daemon using TCP.
|
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
|
||||||
providers:
|
|
||||||
docker:
|
|
||||||
endpoint: "tcp://127.0.0.1:2375"
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
|
||||||
[providers.docker]
|
|
||||||
endpoint = "tcp://127.0.0.1:2375"
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab="CLI"
|
|
||||||
--providers.docker.endpoint=tcp://127.0.0.1:2375
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
providers:
|
providers:
|
||||||
docker:
|
docker:
|
||||||
|
@ -276,56 +231,6 @@ providers:
|
||||||
--providers.docker.endpoint=unix:///var/run/docker.sock
|
--providers.docker.endpoint=unix:///var/run/docker.sock
|
||||||
```
|
```
|
||||||
|
|
||||||
### `username`
|
|
||||||
|
|
||||||
_Optional, Default=""_
|
|
||||||
|
|
||||||
Defines the username for Basic HTTP authentication.
|
|
||||||
This should be used when the Docker daemon socket is exposed through an HTTP proxy that requires Basic HTTP authentication.
|
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
|
||||||
providers:
|
|
||||||
docker:
|
|
||||||
username: foo
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
|
||||||
[providers.docker]
|
|
||||||
username = "foo"
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab="CLI"
|
|
||||||
--providers.docker.username="foo"
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
### `password`
|
|
||||||
|
|
||||||
_Optional, Default=""_
|
|
||||||
|
|
||||||
Defines the password for Basic HTTP authentication.
|
|
||||||
This should be used when the Docker daemon socket is exposed through an HTTP proxy that requires Basic HTTP authentication.
|
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
|
||||||
providers:
|
|
||||||
docker:
|
|
||||||
password: foo
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
|
||||||
[providers.docker]
|
|
||||||
password = "foo"
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab="CLI"
|
|
||||||
--providers.docker.password="foo"
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
### `useBindPortIP`
|
### `useBindPortIP`
|
||||||
|
|
||||||
_Optional, Default=false_
|
_Optional, Default=false_
|
||||||
|
|
|
@ -84,7 +84,6 @@ Defines custom headers to be sent to the endpoint.
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
providers:
|
providers:
|
||||||
http:
|
|
||||||
headers:
|
headers:
|
||||||
name: value
|
name: value
|
||||||
```
|
```
|
||||||
|
@ -96,7 +95,6 @@ providers:
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.http.headers.name=value
|
--providers.http.headers.name=value
|
||||||
```
|
|
||||||
|
|
||||||
### `tls`
|
### `tls`
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ 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 version [v1.1.0](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.1.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).
|
||||||
|
|
||||||
|
@ -26,8 +26,8 @@ For more details, check out the conformance [report](https://github.com/kubernet
|
||||||
1. Install/update the Kubernetes Gateway API CRDs.
|
1. Install/update the Kubernetes Gateway API CRDs.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Install Gateway API CRDs from the Standard channel.
|
# Install Gateway API CRDs from the Experimental channel.
|
||||||
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.1.0/standard-install.yaml
|
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.1.0/experimental-install.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Install/update the Traefik [RBAC](../reference/dynamic-configuration/kubernetes-gateway.md#rbac).
|
2. Install/update the Traefik [RBAC](../reference/dynamic-configuration/kubernetes-gateway.md#rbac).
|
||||||
|
@ -269,15 +269,6 @@ providers:
|
||||||
--providers.kubernetesgateway.experimentalchannel=true
|
--providers.kubernetesgateway.experimentalchannel=true
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! info "Experimental Channel"
|
|
||||||
|
|
||||||
When enabling experimental channel resources support, the experimental CRDs (Custom Resource Definitions) needs to be deployed too.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install Gateway API CRDs from the Experimental channel.
|
|
||||||
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.1.0/experimental-install.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
### `labelselector`
|
### `labelselector`
|
||||||
|
|
||||||
_Optional, Default: ""_
|
_Optional, Default: ""_
|
||||||
|
|
|
@ -287,11 +287,6 @@ providers:
|
||||||
|
|
||||||
_Optional, Default: false_
|
_Optional, Default: false_
|
||||||
|
|
||||||
??? warning "Deprecated"
|
|
||||||
|
|
||||||
The Kubernetes Ingress provider option `disableIngressClassLookup` has been deprecated in v3.1, and will be removed in the next major version.
|
|
||||||
Please use the `disableClusterScopeResources` option instead.
|
|
||||||
|
|
||||||
If the parameter is set to `true`,
|
If the parameter is set to `true`,
|
||||||
Traefik will not discover IngressClasses in the cluster.
|
Traefik will not discover IngressClasses in the cluster.
|
||||||
By doing so, it alleviates the requirement of giving Traefik the rights to look IngressClasses up.
|
By doing so, it alleviates the requirement of giving Traefik the rights to look IngressClasses up.
|
||||||
|
@ -317,33 +312,6 @@ providers:
|
||||||
--providers.kubernetesingress.disableingressclasslookup=true
|
--providers.kubernetesingress.disableingressclasslookup=true
|
||||||
```
|
```
|
||||||
|
|
||||||
### `disableClusterScopeResources`
|
|
||||||
|
|
||||||
_Optional, Default: false_
|
|
||||||
|
|
||||||
When this parameter is set to `true`,
|
|
||||||
Traefik will not discover cluster scope resources (`IngressClass` and `Nodes`).
|
|
||||||
By doing so, it alleviates the requirement of giving Traefik the rights to look up for cluster resources.
|
|
||||||
Furthermore, Traefik will not handle Ingresses with IngressClass references, therefore such Ingresses will be ignored (please note that annotations are not affected by this option).
|
|
||||||
This will also prevent from using the `NodePortLB` options on services.
|
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
|
||||||
providers:
|
|
||||||
kubernetesIngress:
|
|
||||||
disableClusterScopeResources: true
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
|
||||||
[providers.kubernetesIngress]
|
|
||||||
disableClusterScopeResources = true
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab="CLI"
|
|
||||||
--providers.kubernetesingress.disableClusterScopeResources=true
|
|
||||||
```
|
|
||||||
|
|
||||||
### `ingressEndpoint`
|
### `ingressEndpoint`
|
||||||
|
|
||||||
#### `hostname`
|
#### `hostname`
|
||||||
|
|
|
@ -151,7 +151,6 @@ You can specify which Docker API Endpoint to use with the directive [`endpoint`]
|
||||||
It allows scheduling of Traefik on worker nodes, with only the "socket exposer" container on the manager nodes.
|
It allows scheduling of Traefik on worker nodes, with only the "socket exposer" container on the manager nodes.
|
||||||
- Accounting at kernel level, by enforcing kernel calls with mechanisms like [SELinux](https://en.wikipedia.org/wiki/Security-Enhanced_Linux), to only allows an identified set of actions for Traefik's process (or the "socket exposer" process).
|
- Accounting at kernel level, by enforcing kernel calls with mechanisms like [SELinux](https://en.wikipedia.org/wiki/Security-Enhanced_Linux), to only allows an identified set of actions for Traefik's process (or the "socket exposer" process).
|
||||||
- SSH public key authentication (SSH is supported with Docker > 18.09)
|
- SSH public key authentication (SSH is supported with Docker > 18.09)
|
||||||
- Authentication using HTTP Basic authentication through an HTTP proxy that exposes the Docker daemon socket.
|
|
||||||
|
|
||||||
??? info "More Resources and Examples"
|
??? info "More Resources and Examples"
|
||||||
|
|
||||||
|
@ -263,50 +262,6 @@ See the [Docker Swarm API Access](#docker-api-access) section for more informati
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
??? example "Using HTTP"
|
|
||||||
|
|
||||||
Using Docker Engine API you can connect Traefik to remote daemon using HTTP.
|
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
|
||||||
providers:
|
|
||||||
swarm:
|
|
||||||
endpoint: "http://127.0.0.1:2375"
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
|
||||||
[providers.swarm]
|
|
||||||
swarm = "http://127.0.0.1:2375"
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab="CLI"
|
|
||||||
--providers.swarm.endpoint=http://127.0.0.1:2375
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
??? example "Using TCP"
|
|
||||||
|
|
||||||
Using Docker Engine API you can connect Traefik to remote daemon using TCP.
|
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
|
||||||
providers:
|
|
||||||
swarm:
|
|
||||||
endpoint: "tcp://127.0.0.1:2375"
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
|
||||||
[providers.swarm]
|
|
||||||
swarm = "tcp://127.0.0.1:2375"
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab="CLI"
|
|
||||||
--providers.swarm.endpoint=tcp://127.0.0.1:2375
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
providers:
|
providers:
|
||||||
swarm:
|
swarm:
|
||||||
|
@ -322,56 +277,6 @@ providers:
|
||||||
--providers.swarm.endpoint=unix:///var/run/docker.sock
|
--providers.swarm.endpoint=unix:///var/run/docker.sock
|
||||||
```
|
```
|
||||||
|
|
||||||
### `username`
|
|
||||||
|
|
||||||
_Optional, Default=""_
|
|
||||||
|
|
||||||
Defines the username for Basic HTTP authentication.
|
|
||||||
This should be used when the Docker daemon socket is exposed through an HTTP proxy that requires Basic HTTP authentication.
|
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
|
||||||
providers:
|
|
||||||
swarm:
|
|
||||||
username: foo
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
|
||||||
[providers.swarm]
|
|
||||||
username = "foo"
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab="CLI"
|
|
||||||
--providers.swarm.username="foo"
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
### `password`
|
|
||||||
|
|
||||||
_Optional, Default=""_
|
|
||||||
|
|
||||||
Defines the password for Basic HTTP authentication.
|
|
||||||
This should be used when the Docker daemon socket is exposed through an HTTP proxy that requires Basic HTTP authentication.
|
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
|
||||||
providers:
|
|
||||||
swarm:
|
|
||||||
password: foo
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
|
||||||
[providers.swarm]
|
|
||||||
password = "foo"
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab="CLI"
|
|
||||||
--providers.swarm.password="foo"
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
### `useBindPortIP`
|
### `useBindPortIP`
|
||||||
|
|
||||||
_Optional, Default=false_
|
_Optional, Default=false_
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
- "traefik.http.middlewares.middleware05.circuitbreaker.responsecode=42"
|
- "traefik.http.middlewares.middleware05.circuitbreaker.responsecode=42"
|
||||||
- "traefik.http.middlewares.middleware06.compress=true"
|
- "traefik.http.middlewares.middleware06.compress=true"
|
||||||
- "traefik.http.middlewares.middleware06.compress.defaultencoding=foobar"
|
- "traefik.http.middlewares.middleware06.compress.defaultencoding=foobar"
|
||||||
- "traefik.http.middlewares.middleware06.compress.encodings=foobar, foobar"
|
|
||||||
- "traefik.http.middlewares.middleware06.compress.excludedcontenttypes=foobar, foobar"
|
- "traefik.http.middlewares.middleware06.compress.excludedcontenttypes=foobar, foobar"
|
||||||
- "traefik.http.middlewares.middleware06.compress.includedcontenttypes=foobar, foobar"
|
- "traefik.http.middlewares.middleware06.compress.includedcontenttypes=foobar, foobar"
|
||||||
- "traefik.http.middlewares.middleware06.compress.minresponsebodybytes=42"
|
- "traefik.http.middlewares.middleware06.compress.minresponsebodybytes=42"
|
||||||
|
|
|
@ -143,7 +143,6 @@
|
||||||
excludedContentTypes = ["foobar", "foobar"]
|
excludedContentTypes = ["foobar", "foobar"]
|
||||||
includedContentTypes = ["foobar", "foobar"]
|
includedContentTypes = ["foobar", "foobar"]
|
||||||
minResponseBodyBytes = 42
|
minResponseBodyBytes = 42
|
||||||
encodings = ["foobar", "foobar"]
|
|
||||||
defaultEncoding = "foobar"
|
defaultEncoding = "foobar"
|
||||||
[http.middlewares.Middleware07]
|
[http.middlewares.Middleware07]
|
||||||
[http.middlewares.Middleware07.contentType]
|
[http.middlewares.Middleware07.contentType]
|
||||||
|
|
|
@ -152,9 +152,6 @@ http:
|
||||||
- foobar
|
- foobar
|
||||||
- foobar
|
- foobar
|
||||||
minResponseBodyBytes: 42
|
minResponseBodyBytes: 42
|
||||||
encodings:
|
|
||||||
- foobar
|
|
||||||
- foobar
|
|
||||||
defaultEncoding: foobar
|
defaultEncoding: foobar
|
||||||
Middleware07:
|
Middleware07:
|
||||||
contentType:
|
contentType:
|
||||||
|
|
|
@ -904,7 +904,7 @@ spec:
|
||||||
compress:
|
compress:
|
||||||
description: |-
|
description: |-
|
||||||
Compress holds the compress middleware configuration.
|
Compress holds the compress middleware configuration.
|
||||||
This middleware compresses responses before sending them to the client, using gzip, brotli, or zstd compression.
|
This middleware compresses responses before sending them to the client, using gzip compression.
|
||||||
More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/compress/
|
More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/compress/
|
||||||
properties:
|
properties:
|
||||||
defaultEncoding:
|
defaultEncoding:
|
||||||
|
@ -912,12 +912,6 @@ spec:
|
||||||
the `Accept-Encoding` header is not in the request or contains
|
the `Accept-Encoding` header is not in the request or contains
|
||||||
a wildcard (`*`).
|
a wildcard (`*`).
|
||||||
type: string
|
type: string
|
||||||
encodings:
|
|
||||||
description: Encodings defines the list of supported compression
|
|
||||||
algorithms.
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
type: array
|
|
||||||
excludedContentTypes:
|
excludedContentTypes:
|
||||||
description: |-
|
description: |-
|
||||||
ExcludedContentTypes defines the list of content types to compare the Content-Type header of the incoming requests and responses before compressing.
|
ExcludedContentTypes defines the list of content types to compare the Content-Type header of the incoming requests and responses before compressing.
|
||||||
|
|
|
@ -22,8 +22,6 @@ THIS FILE MUST NOT BE EDITED BY HAND
|
||||||
| `traefik/http/middlewares/Middleware05/circuitBreaker/recoveryDuration` | `42s` |
|
| `traefik/http/middlewares/Middleware05/circuitBreaker/recoveryDuration` | `42s` |
|
||||||
| `traefik/http/middlewares/Middleware05/circuitBreaker/responseCode` | `42` |
|
| `traefik/http/middlewares/Middleware05/circuitBreaker/responseCode` | `42` |
|
||||||
| `traefik/http/middlewares/Middleware06/compress/defaultEncoding` | `foobar` |
|
| `traefik/http/middlewares/Middleware06/compress/defaultEncoding` | `foobar` |
|
||||||
| `traefik/http/middlewares/Middleware06/compress/encodings/0` | `foobar` |
|
|
||||||
| `traefik/http/middlewares/Middleware06/compress/encodings/1` | `foobar` |
|
|
||||||
| `traefik/http/middlewares/Middleware06/compress/excludedContentTypes/0` | `foobar` |
|
| `traefik/http/middlewares/Middleware06/compress/excludedContentTypes/0` | `foobar` |
|
||||||
| `traefik/http/middlewares/Middleware06/compress/excludedContentTypes/1` | `foobar` |
|
| `traefik/http/middlewares/Middleware06/compress/excludedContentTypes/1` | `foobar` |
|
||||||
| `traefik/http/middlewares/Middleware06/compress/includedContentTypes/0` | `foobar` |
|
| `traefik/http/middlewares/Middleware06/compress/includedContentTypes/0` | `foobar` |
|
||||||
|
|
|
@ -180,7 +180,7 @@ spec:
|
||||||
compress:
|
compress:
|
||||||
description: |-
|
description: |-
|
||||||
Compress holds the compress middleware configuration.
|
Compress holds the compress middleware configuration.
|
||||||
This middleware compresses responses before sending them to the client, using gzip, brotli, or zstd compression.
|
This middleware compresses responses before sending them to the client, using gzip compression.
|
||||||
More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/compress/
|
More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/compress/
|
||||||
properties:
|
properties:
|
||||||
defaultEncoding:
|
defaultEncoding:
|
||||||
|
@ -188,12 +188,6 @@ spec:
|
||||||
the `Accept-Encoding` header is not in the request or contains
|
the `Accept-Encoding` header is not in the request or contains
|
||||||
a wildcard (`*`).
|
a wildcard (`*`).
|
||||||
type: string
|
type: string
|
||||||
encodings:
|
|
||||||
description: Encodings defines the list of supported compression
|
|
||||||
algorithms.
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
type: array
|
|
||||||
excludedContentTypes:
|
excludedContentTypes:
|
||||||
description: |-
|
description: |-
|
||||||
ExcludedContentTypes defines the list of content types to compare the Content-Type header of the incoming requests and responses before compressing.
|
ExcludedContentTypes defines the list of content types to compare the Content-Type header of the incoming requests and responses before compressing.
|
||||||
|
|
|
@ -591,9 +591,6 @@ Client timeout for HTTP connections. (Default: ```0```)
|
||||||
`--providers.docker.network`:
|
`--providers.docker.network`:
|
||||||
Default Docker network used.
|
Default Docker network used.
|
||||||
|
|
||||||
`--providers.docker.password`:
|
|
||||||
Password for Basic HTTP authentication.
|
|
||||||
|
|
||||||
`--providers.docker.tls.ca`:
|
`--providers.docker.tls.ca`:
|
||||||
TLS CA
|
TLS CA
|
||||||
|
|
||||||
|
@ -609,9 +606,6 @@ TLS key
|
||||||
`--providers.docker.usebindportip`:
|
`--providers.docker.usebindportip`:
|
||||||
Use the ip address from the bound port, rather than from the inner network. (Default: ```false```)
|
Use the ip address from the bound port, rather than from the inner network. (Default: ```false```)
|
||||||
|
|
||||||
`--providers.docker.username`:
|
|
||||||
Username for Basic HTTP authentication.
|
|
||||||
|
|
||||||
`--providers.docker.watch`:
|
`--providers.docker.watch`:
|
||||||
Watch Docker events. (Default: ```true```)
|
Watch Docker events. (Default: ```true```)
|
||||||
|
|
||||||
|
@ -732,9 +726,6 @@ Allow ExternalName services. (Default: ```false```)
|
||||||
`--providers.kubernetescrd.certauthfilepath`:
|
`--providers.kubernetescrd.certauthfilepath`:
|
||||||
Kubernetes certificate authority file path (not needed for in-cluster client).
|
Kubernetes certificate authority file path (not needed for in-cluster client).
|
||||||
|
|
||||||
`--providers.kubernetescrd.disableclusterscoperesources`:
|
|
||||||
Disables the lookup of cluster scope resources (incompatible with IngressClasses and NodePortLB enabled services). (Default: ```false```)
|
|
||||||
|
|
||||||
`--providers.kubernetescrd.endpoint`:
|
`--providers.kubernetescrd.endpoint`:
|
||||||
Kubernetes server endpoint (required for external cluster client).
|
Kubernetes server endpoint (required for external cluster client).
|
||||||
|
|
||||||
|
@ -807,11 +798,8 @@ Allow ExternalName services. (Default: ```false```)
|
||||||
`--providers.kubernetesingress.certauthfilepath`:
|
`--providers.kubernetesingress.certauthfilepath`:
|
||||||
Kubernetes certificate authority file path (not needed for in-cluster client).
|
Kubernetes certificate authority file path (not needed for in-cluster client).
|
||||||
|
|
||||||
`--providers.kubernetesingress.disableclusterscoperesources`:
|
|
||||||
Disables the lookup of cluster scope resources (incompatible with IngressClasses and NodePortLB enabled services). (Default: ```false```)
|
|
||||||
|
|
||||||
`--providers.kubernetesingress.disableingressclasslookup`:
|
`--providers.kubernetesingress.disableingressclasslookup`:
|
||||||
Disables the lookup of IngressClasses (Deprecated, please use DisableClusterScopeResources). (Default: ```false```)
|
Disables the lookup of IngressClasses. (Default: ```false```)
|
||||||
|
|
||||||
`--providers.kubernetesingress.endpoint`:
|
`--providers.kubernetesingress.endpoint`:
|
||||||
Kubernetes server endpoint (required for external cluster client).
|
Kubernetes server endpoint (required for external cluster client).
|
||||||
|
@ -981,9 +969,6 @@ Client timeout for HTTP connections. (Default: ```0```)
|
||||||
`--providers.swarm.network`:
|
`--providers.swarm.network`:
|
||||||
Default Docker network used.
|
Default Docker network used.
|
||||||
|
|
||||||
`--providers.swarm.password`:
|
|
||||||
Password for Basic HTTP authentication.
|
|
||||||
|
|
||||||
`--providers.swarm.refreshseconds`:
|
`--providers.swarm.refreshseconds`:
|
||||||
Polling interval for swarm mode. (Default: ```15```)
|
Polling interval for swarm mode. (Default: ```15```)
|
||||||
|
|
||||||
|
@ -1002,9 +987,6 @@ TLS key
|
||||||
`--providers.swarm.usebindportip`:
|
`--providers.swarm.usebindportip`:
|
||||||
Use the ip address from the bound port, rather than from the inner network. (Default: ```false```)
|
Use the ip address from the bound port, rather than from the inner network. (Default: ```false```)
|
||||||
|
|
||||||
`--providers.swarm.username`:
|
|
||||||
Username for Basic HTTP authentication.
|
|
||||||
|
|
||||||
`--providers.swarm.watch`:
|
`--providers.swarm.watch`:
|
||||||
Watch Docker events. (Default: ```true```)
|
Watch Docker events. (Default: ```true```)
|
||||||
|
|
||||||
|
|
|
@ -591,9 +591,6 @@ Client timeout for HTTP connections. (Default: ```0```)
|
||||||
`TRAEFIK_PROVIDERS_DOCKER_NETWORK`:
|
`TRAEFIK_PROVIDERS_DOCKER_NETWORK`:
|
||||||
Default Docker network used.
|
Default Docker network used.
|
||||||
|
|
||||||
`TRAEFIK_PROVIDERS_DOCKER_PASSWORD`:
|
|
||||||
Password for Basic HTTP authentication.
|
|
||||||
|
|
||||||
`TRAEFIK_PROVIDERS_DOCKER_TLS_CA`:
|
`TRAEFIK_PROVIDERS_DOCKER_TLS_CA`:
|
||||||
TLS CA
|
TLS CA
|
||||||
|
|
||||||
|
@ -609,9 +606,6 @@ TLS key
|
||||||
`TRAEFIK_PROVIDERS_DOCKER_USEBINDPORTIP`:
|
`TRAEFIK_PROVIDERS_DOCKER_USEBINDPORTIP`:
|
||||||
Use the ip address from the bound port, rather than from the inner network. (Default: ```false```)
|
Use the ip address from the bound port, rather than from the inner network. (Default: ```false```)
|
||||||
|
|
||||||
`TRAEFIK_PROVIDERS_DOCKER_USERNAME`:
|
|
||||||
Username for Basic HTTP authentication.
|
|
||||||
|
|
||||||
`TRAEFIK_PROVIDERS_DOCKER_WATCH`:
|
`TRAEFIK_PROVIDERS_DOCKER_WATCH`:
|
||||||
Watch Docker events. (Default: ```true```)
|
Watch Docker events. (Default: ```true```)
|
||||||
|
|
||||||
|
@ -732,9 +726,6 @@ Allow ExternalName services. (Default: ```false```)
|
||||||
`TRAEFIK_PROVIDERS_KUBERNETESCRD_CERTAUTHFILEPATH`:
|
`TRAEFIK_PROVIDERS_KUBERNETESCRD_CERTAUTHFILEPATH`:
|
||||||
Kubernetes certificate authority file path (not needed for in-cluster client).
|
Kubernetes certificate authority file path (not needed for in-cluster client).
|
||||||
|
|
||||||
`TRAEFIK_PROVIDERS_KUBERNETESCRD_DISABLECLUSTERSCOPERESOURCES`:
|
|
||||||
Disables the lookup of cluster scope resources (incompatible with IngressClasses and NodePortLB enabled services). (Default: ```false```)
|
|
||||||
|
|
||||||
`TRAEFIK_PROVIDERS_KUBERNETESCRD_ENDPOINT`:
|
`TRAEFIK_PROVIDERS_KUBERNETESCRD_ENDPOINT`:
|
||||||
Kubernetes server endpoint (required for external cluster client).
|
Kubernetes server endpoint (required for external cluster client).
|
||||||
|
|
||||||
|
@ -807,11 +798,8 @@ Allow ExternalName services. (Default: ```false```)
|
||||||
`TRAEFIK_PROVIDERS_KUBERNETESINGRESS_CERTAUTHFILEPATH`:
|
`TRAEFIK_PROVIDERS_KUBERNETESINGRESS_CERTAUTHFILEPATH`:
|
||||||
Kubernetes certificate authority file path (not needed for in-cluster client).
|
Kubernetes certificate authority file path (not needed for in-cluster client).
|
||||||
|
|
||||||
`TRAEFIK_PROVIDERS_KUBERNETESINGRESS_DISABLECLUSTERSCOPERESOURCES`:
|
|
||||||
Disables the lookup of cluster scope resources (incompatible with IngressClasses and NodePortLB enabled services). (Default: ```false```)
|
|
||||||
|
|
||||||
`TRAEFIK_PROVIDERS_KUBERNETESINGRESS_DISABLEINGRESSCLASSLOOKUP`:
|
`TRAEFIK_PROVIDERS_KUBERNETESINGRESS_DISABLEINGRESSCLASSLOOKUP`:
|
||||||
Disables the lookup of IngressClasses (Deprecated, please use DisableClusterScopeResources). (Default: ```false```)
|
Disables the lookup of IngressClasses. (Default: ```false```)
|
||||||
|
|
||||||
`TRAEFIK_PROVIDERS_KUBERNETESINGRESS_ENDPOINT`:
|
`TRAEFIK_PROVIDERS_KUBERNETESINGRESS_ENDPOINT`:
|
||||||
Kubernetes server endpoint (required for external cluster client).
|
Kubernetes server endpoint (required for external cluster client).
|
||||||
|
@ -981,9 +969,6 @@ Client timeout for HTTP connections. (Default: ```0```)
|
||||||
`TRAEFIK_PROVIDERS_SWARM_NETWORK`:
|
`TRAEFIK_PROVIDERS_SWARM_NETWORK`:
|
||||||
Default Docker network used.
|
Default Docker network used.
|
||||||
|
|
||||||
`TRAEFIK_PROVIDERS_SWARM_PASSWORD`:
|
|
||||||
Password for Basic HTTP authentication.
|
|
||||||
|
|
||||||
`TRAEFIK_PROVIDERS_SWARM_REFRESHSECONDS`:
|
`TRAEFIK_PROVIDERS_SWARM_REFRESHSECONDS`:
|
||||||
Polling interval for swarm mode. (Default: ```15```)
|
Polling interval for swarm mode. (Default: ```15```)
|
||||||
|
|
||||||
|
@ -1002,9 +987,6 @@ TLS key
|
||||||
`TRAEFIK_PROVIDERS_SWARM_USEBINDPORTIP`:
|
`TRAEFIK_PROVIDERS_SWARM_USEBINDPORTIP`:
|
||||||
Use the ip address from the bound port, rather than from the inner network. (Default: ```false```)
|
Use the ip address from the bound port, rather than from the inner network. (Default: ```false```)
|
||||||
|
|
||||||
`TRAEFIK_PROVIDERS_SWARM_USERNAME`:
|
|
||||||
Username for Basic HTTP authentication.
|
|
||||||
|
|
||||||
`TRAEFIK_PROVIDERS_SWARM_WATCH`:
|
`TRAEFIK_PROVIDERS_SWARM_WATCH`:
|
||||||
Watch Docker events. (Default: ```true```)
|
Watch Docker events. (Default: ```true```)
|
||||||
|
|
||||||
|
|
|
@ -85,8 +85,6 @@
|
||||||
useBindPortIP = true
|
useBindPortIP = true
|
||||||
watch = true
|
watch = true
|
||||||
defaultRule = "foobar"
|
defaultRule = "foobar"
|
||||||
username = "foobar"
|
|
||||||
password = "foobar"
|
|
||||||
endpoint = "foobar"
|
endpoint = "foobar"
|
||||||
httpClientTimeout = "42s"
|
httpClientTimeout = "42s"
|
||||||
[providers.docker.tls]
|
[providers.docker.tls]
|
||||||
|
@ -102,8 +100,6 @@
|
||||||
useBindPortIP = true
|
useBindPortIP = true
|
||||||
watch = true
|
watch = true
|
||||||
defaultRule = "foobar"
|
defaultRule = "foobar"
|
||||||
username = "foobar"
|
|
||||||
password = "foobar"
|
|
||||||
endpoint = "foobar"
|
endpoint = "foobar"
|
||||||
httpClientTimeout = "42s"
|
httpClientTimeout = "42s"
|
||||||
refreshSeconds = "42s"
|
refreshSeconds = "42s"
|
||||||
|
@ -128,7 +124,6 @@
|
||||||
allowEmptyServices = true
|
allowEmptyServices = true
|
||||||
allowExternalNameServices = true
|
allowExternalNameServices = true
|
||||||
disableIngressClassLookup = true
|
disableIngressClassLookup = true
|
||||||
disableClusterScopeResources = true
|
|
||||||
nativeLBByDefault = true
|
nativeLBByDefault = true
|
||||||
[providers.kubernetesIngress.ingressEndpoint]
|
[providers.kubernetesIngress.ingressEndpoint]
|
||||||
ip = "foobar"
|
ip = "foobar"
|
||||||
|
@ -146,7 +141,6 @@
|
||||||
throttleDuration = "42s"
|
throttleDuration = "42s"
|
||||||
allowEmptyServices = true
|
allowEmptyServices = true
|
||||||
nativeLBByDefault = true
|
nativeLBByDefault = true
|
||||||
disableClusterScopeResources = true
|
|
||||||
[providers.kubernetesGateway]
|
[providers.kubernetesGateway]
|
||||||
endpoint = "foobar"
|
endpoint = "foobar"
|
||||||
token = "foobar"
|
token = "foobar"
|
||||||
|
|
|
@ -96,8 +96,6 @@ providers:
|
||||||
useBindPortIP: true
|
useBindPortIP: true
|
||||||
watch: true
|
watch: true
|
||||||
defaultRule: foobar
|
defaultRule: foobar
|
||||||
username: foobar
|
|
||||||
password: foobar
|
|
||||||
endpoint: foobar
|
endpoint: foobar
|
||||||
tls:
|
tls:
|
||||||
ca: foobar
|
ca: foobar
|
||||||
|
@ -113,8 +111,6 @@ providers:
|
||||||
useBindPortIP: true
|
useBindPortIP: true
|
||||||
watch: true
|
watch: true
|
||||||
defaultRule: foobar
|
defaultRule: foobar
|
||||||
username: foobar
|
|
||||||
password: foobar
|
|
||||||
endpoint: foobar
|
endpoint: foobar
|
||||||
tls:
|
tls:
|
||||||
ca: foobar
|
ca: foobar
|
||||||
|
@ -145,7 +141,6 @@ providers:
|
||||||
allowEmptyServices: true
|
allowEmptyServices: true
|
||||||
allowExternalNameServices: true
|
allowExternalNameServices: true
|
||||||
disableIngressClassLookup: true
|
disableIngressClassLookup: true
|
||||||
disableClusterScopeResources: true
|
|
||||||
nativeLBByDefault: true
|
nativeLBByDefault: true
|
||||||
kubernetesCRD:
|
kubernetesCRD:
|
||||||
endpoint: foobar
|
endpoint: foobar
|
||||||
|
@ -161,7 +156,6 @@ providers:
|
||||||
throttleDuration: 42s
|
throttleDuration: 42s
|
||||||
allowEmptyServices: true
|
allowEmptyServices: true
|
||||||
nativeLBByDefault: true
|
nativeLBByDefault: true
|
||||||
disableClusterScopeResources: true
|
|
||||||
kubernetesGateway:
|
kubernetesGateway:
|
||||||
endpoint: foobar
|
endpoint: foobar
|
||||||
token: foobar
|
token: foobar
|
||||||
|
|
20
go.mod
20
go.mod
|
@ -12,8 +12,8 @@ require (
|
||||||
github.com/containous/alice v0.0.0-20181107144136-d83ebdd94cbd // No tag on the repo.
|
github.com/containous/alice v0.0.0-20181107144136-d83ebdd94cbd // No tag on the repo.
|
||||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e // No tag on the repo.
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e // No tag on the repo.
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0
|
github.com/coreos/go-systemd/v22 v22.5.0
|
||||||
github.com/docker/cli v27.1.1+incompatible
|
github.com/docker/cli v24.0.9+incompatible
|
||||||
github.com/docker/docker v27.1.1+incompatible
|
github.com/docker/docker v27.0.3+incompatible
|
||||||
github.com/docker/go-connections v0.5.0
|
github.com/docker/go-connections v0.5.0
|
||||||
github.com/fatih/structs v1.1.0
|
github.com/fatih/structs v1.1.0
|
||||||
github.com/fsnotify/fsnotify v1.7.0
|
github.com/fsnotify/fsnotify v1.7.0
|
||||||
|
@ -22,7 +22,7 @@ require (
|
||||||
github.com/go-kit/log v0.2.1
|
github.com/go-kit/log v0.2.1
|
||||||
github.com/golang/protobuf v1.5.4
|
github.com/golang/protobuf v1.5.4
|
||||||
github.com/google/go-github/v28 v28.1.1
|
github.com/google/go-github/v28 v28.1.1
|
||||||
github.com/gorilla/mux v1.8.1
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/gorilla/websocket v1.5.1
|
github.com/gorilla/websocket v1.5.1
|
||||||
github.com/hashicorp/consul/api v1.26.1
|
github.com/hashicorp/consul/api v1.26.1
|
||||||
github.com/hashicorp/go-hclog v1.6.3
|
github.com/hashicorp/go-hclog v1.6.3
|
||||||
|
@ -34,7 +34,7 @@ require (
|
||||||
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/juliens/wasm-goexport v0.0.6
|
github.com/juliens/wasm-goexport v0.0.6
|
||||||
github.com/klauspost/compress v1.17.9
|
github.com/klauspost/compress v1.17.4
|
||||||
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
|
||||||
|
@ -129,7 +129,7 @@ require (
|
||||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||||
github.com/Masterminds/semver/v3 v3.2.1 // indirect
|
github.com/Masterminds/semver/v3 v3.2.1 // indirect
|
||||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
github.com/Microsoft/hcsshim v0.11.7 // indirect
|
github.com/Microsoft/hcsshim v0.11.5 // indirect
|
||||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
|
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
|
||||||
github.com/VividCortex/gohistogram v1.0.0 // indirect
|
github.com/VividCortex/gohistogram v1.0.0 // indirect
|
||||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect
|
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect
|
||||||
|
@ -157,9 +157,9 @@ require (
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
github.com/civo/civogo v0.3.11 // indirect
|
github.com/civo/civogo v0.3.11 // indirect
|
||||||
github.com/cloudflare/cloudflare-go v0.97.0 // indirect
|
github.com/cloudflare/cloudflare-go v0.97.0 // indirect
|
||||||
github.com/containerd/containerd v1.7.20 // indirect
|
github.com/containerd/containerd v1.7.18 // indirect
|
||||||
|
github.com/containerd/errdefs v0.1.0 // indirect
|
||||||
github.com/containerd/log v0.1.0 // indirect
|
github.com/containerd/log v0.1.0 // indirect
|
||||||
github.com/containerd/platforms v0.2.1 // indirect
|
|
||||||
github.com/coreos/go-semver v0.3.1 // indirect
|
github.com/coreos/go-semver v0.3.1 // indirect
|
||||||
github.com/cpu/goacmedns v0.1.1 // indirect
|
github.com/cpu/goacmedns v0.1.1 // indirect
|
||||||
github.com/cpuguy83/dockercfg v0.3.1 // indirect
|
github.com/cpuguy83/dockercfg v0.3.1 // indirect
|
||||||
|
@ -194,7 +194,7 @@ require (
|
||||||
github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
|
github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
|
||||||
github.com/go-zookeeper/zk v1.0.3 // indirect
|
github.com/go-zookeeper/zk v1.0.3 // indirect
|
||||||
github.com/goccy/go-json v0.10.3 // indirect
|
github.com/goccy/go-json v0.10.3 // indirect
|
||||||
github.com/gofrs/flock v0.12.0 // indirect
|
github.com/gofrs/flock v0.8.1 // indirect
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible // indirect
|
github.com/gofrs/uuid v4.4.0+incompatible // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||||
|
@ -255,7 +255,7 @@ require (
|
||||||
github.com/moby/patternmatcher v0.6.0 // indirect
|
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||||
github.com/moby/spdystream v0.2.0 // indirect
|
github.com/moby/spdystream v0.2.0 // indirect
|
||||||
github.com/moby/sys/sequential v0.5.0 // indirect
|
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||||
github.com/moby/sys/user v0.2.0 // indirect
|
github.com/moby/sys/user v0.1.0 // indirect
|
||||||
github.com/moby/term v0.5.0 // indirect
|
github.com/moby/term v0.5.0 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
@ -287,7 +287,7 @@ require (
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||||
github.com/pquerna/otp v1.4.0 // indirect
|
github.com/pquerna/otp v1.4.0 // indirect
|
||||||
github.com/prometheus/common v0.48.0 // indirect
|
github.com/prometheus/common v0.48.0 // indirect
|
||||||
github.com/prometheus/procfs v0.15.1 // indirect
|
github.com/prometheus/procfs v0.12.0 // indirect
|
||||||
github.com/quic-go/qpack v0.4.0 // indirect
|
github.com/quic-go/qpack v0.4.0 // indirect
|
||||||
github.com/redis/go-redis/v9 v9.2.1 // indirect
|
github.com/redis/go-redis/v9 v9.2.1 // indirect
|
||||||
github.com/rs/cors v1.7.0 // indirect
|
github.com/rs/cors v1.7.0 // indirect
|
||||||
|
|
35
go.sum
35
go.sum
|
@ -82,8 +82,8 @@ github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v
|
||||||
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
|
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
|
||||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||||
github.com/Microsoft/hcsshim v0.11.7 h1:vl/nj3Bar/CvJSYo7gIQPyRWc9f3c6IeSNavBTSZNZQ=
|
github.com/Microsoft/hcsshim v0.11.5 h1:haEcLNpj9Ka1gd3B3tAEs9CpE0c+1IhoL59w/exYU38=
|
||||||
github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU=
|
github.com/Microsoft/hcsshim v0.11.5/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 h1:xPMsUicZ3iosVPSIP7bW5EcGUzjiiMl1OYTe14y/R24=
|
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 h1:xPMsUicZ3iosVPSIP7bW5EcGUzjiiMl1OYTe14y/R24=
|
||||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=
|
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=
|
||||||
|
@ -192,12 +192,12 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH
|
||||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
github.com/containerd/containerd v1.7.20 h1:Sl6jQYk3TRavaU83h66QMbI2Nqg9Jm6qzwX57Vsn1SQ=
|
github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao=
|
||||||
github.com/containerd/containerd v1.7.20/go.mod h1:52GsS5CwquuqPuLncsXwG0t2CiUce+KsNHJZQJvAgR0=
|
github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4=
|
||||||
|
github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM=
|
||||||
|
github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0=
|
||||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||||
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
|
|
||||||
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
|
|
||||||
github.com/containous/alice v0.0.0-20181107144136-d83ebdd94cbd h1:0n+lFLh5zU0l6KSk3KpnDwfbPGAR44aRLgTbCnhRBHU=
|
github.com/containous/alice v0.0.0-20181107144136-d83ebdd94cbd h1:0n+lFLh5zU0l6KSk3KpnDwfbPGAR44aRLgTbCnhRBHU=
|
||||||
github.com/containous/alice v0.0.0-20181107144136-d83ebdd94cbd/go.mod h1:BbQgeDS5i0tNvypwEoF1oNjOJw8knRAE1DnVvjDstcQ=
|
github.com/containous/alice v0.0.0-20181107144136-d83ebdd94cbd/go.mod h1:BbQgeDS5i0tNvypwEoF1oNjOJw8knRAE1DnVvjDstcQ=
|
||||||
github.com/containous/go-http-auth v0.4.1-0.20200324110947-a37a7636d23e h1:D+uTEzDZc1Fhmd0Pq06c+O9+KkAyExw0eVmu/NOqaHU=
|
github.com/containous/go-http-auth v0.4.1-0.20200324110947-a37a7636d23e h1:D+uTEzDZc1Fhmd0Pq06c+O9+KkAyExw0eVmu/NOqaHU=
|
||||||
|
@ -247,10 +247,10 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
|
||||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||||
github.com/dnsimple/dnsimple-go v1.7.0 h1:JKu9xJtZ3SqOC+BuYgAWeab7+EEx0sz422vu8j611ZY=
|
github.com/dnsimple/dnsimple-go v1.7.0 h1:JKu9xJtZ3SqOC+BuYgAWeab7+EEx0sz422vu8j611ZY=
|
||||||
github.com/dnsimple/dnsimple-go v1.7.0/go.mod h1:EKpuihlWizqYafSnQHGCd/gyvy3HkEQJ7ODB4KdV8T8=
|
github.com/dnsimple/dnsimple-go v1.7.0/go.mod h1:EKpuihlWizqYafSnQHGCd/gyvy3HkEQJ7ODB4KdV8T8=
|
||||||
github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE=
|
github.com/docker/cli v24.0.9+incompatible h1:OxbimnP/z+qVjDLpq9wbeFU3Nc30XhSe+LkwYQisD50=
|
||||||
github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
github.com/docker/cli v24.0.9+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||||
github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
|
github.com/docker/docker v27.0.3+incompatible h1:aBGI9TeQ4MPlhquTQKq9XbK79rKFVwXNUAYz9aXyEBE=
|
||||||
github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v27.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||||
|
@ -377,9 +377,8 @@ github.com/goccy/go-json v0.7.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGF
|
||||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
||||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||||
github.com/gofrs/flock v0.12.0 h1:xHW8t8GPAiGtqz7KxiSqfOEXwpOaqhpYZrTE2MQBgXY=
|
|
||||||
github.com/gofrs/flock v0.12.0/go.mod h1:FirDy1Ing0mI2+kB6wk+vyyAH+e6xiE+EYA0jnzV9jc=
|
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
@ -600,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.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
||||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||||
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=
|
||||||
|
@ -743,8 +742,8 @@ github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8
|
||||||
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
||||||
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
||||||
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
||||||
github.com/moby/sys/user v0.2.0 h1:OnpapJsRp25vkhw8TFG6OLJODNh/3rEwRWtJ3kakwRM=
|
github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg=
|
||||||
github.com/moby/sys/user v0.2.0/go.mod h1:RYstrcWOJpVh+6qzUqp2bU3eaRpdiQeKGlKitaH0PM8=
|
github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU=
|
||||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
@ -873,8 +872,8 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z
|
||||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||||
|
|
|
@ -904,7 +904,7 @@ spec:
|
||||||
compress:
|
compress:
|
||||||
description: |-
|
description: |-
|
||||||
Compress holds the compress middleware configuration.
|
Compress holds the compress middleware configuration.
|
||||||
This middleware compresses responses before sending them to the client, using gzip, brotli, or zstd compression.
|
This middleware compresses responses before sending them to the client, using gzip compression.
|
||||||
More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/compress/
|
More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/compress/
|
||||||
properties:
|
properties:
|
||||||
defaultEncoding:
|
defaultEncoding:
|
||||||
|
@ -912,12 +912,6 @@ spec:
|
||||||
the `Accept-Encoding` header is not in the request or contains
|
the `Accept-Encoding` header is not in the request or contains
|
||||||
a wildcard (`*`).
|
a wildcard (`*`).
|
||||||
type: string
|
type: string
|
||||||
encodings:
|
|
||||||
description: Encodings defines the list of supported compression
|
|
||||||
algorithms.
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
type: array
|
|
||||||
excludedContentTypes:
|
excludedContentTypes:
|
||||||
description: |-
|
description: |-
|
||||||
ExcludedContentTypes defines the list of content types to compare the Content-Type header of the incoming requests and responses before compressing.
|
ExcludedContentTypes defines the list of content types to compare the Content-Type header of the incoming requests and responses before compressing.
|
||||||
|
|
|
@ -86,8 +86,8 @@ func (s *K8sConformanceSuite) SetupSuite() {
|
||||||
s.T().Fatal("Traefik image is not present")
|
s.T().Fatal("Traefik image is not present")
|
||||||
}
|
}
|
||||||
|
|
||||||
s.k3sContainer, err = k3s.Run(ctx,
|
s.k3sContainer, err = k3s.RunContainer(ctx,
|
||||||
k3sImage,
|
testcontainers.WithImage(k3sImage),
|
||||||
k3s.WithManifest("./fixtures/k8s-conformance/00-experimental-v1.1.0.yml"),
|
k3s.WithManifest("./fixtures/k8s-conformance/00-experimental-v1.1.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"),
|
||||||
|
@ -206,7 +206,6 @@ func (s *K8sConformanceSuite) TestK8sGatewayAPIConformance() {
|
||||||
features.SupportHTTPRouteHostRewrite,
|
features.SupportHTTPRouteHostRewrite,
|
||||||
features.SupportHTTPRoutePathRewrite,
|
features.SupportHTTPRoutePathRewrite,
|
||||||
features.SupportHTTPRoutePathRedirect,
|
features.SupportHTTPRoutePathRedirect,
|
||||||
features.SupportHTTPRouteResponseHeaderModification,
|
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
|
|
|
@ -214,7 +214,6 @@ type providers struct {
|
||||||
ETCD *etcd `json:"etcd,omitempty" toml:"etcd,omitempty" yaml:"etcd,omitempty" label:"allowEmpty" file:"allowEmpty"`
|
ETCD *etcd `json:"etcd,omitempty" toml:"etcd,omitempty" yaml:"etcd,omitempty" label:"allowEmpty" file:"allowEmpty"`
|
||||||
Redis *redis `json:"redis,omitempty" toml:"redis,omitempty" yaml:"redis,omitempty" label:"allowEmpty" file:"allowEmpty"`
|
Redis *redis `json:"redis,omitempty" toml:"redis,omitempty" yaml:"redis,omitempty" label:"allowEmpty" file:"allowEmpty"`
|
||||||
HTTP *http `json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" label:"allowEmpty" file:"allowEmpty"`
|
HTTP *http `json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" label:"allowEmpty" file:"allowEmpty"`
|
||||||
KubernetesIngress *ingress `json:"kubernetesIngress,omitempty" toml:"kubernetesIngress,omitempty" yaml:"kubernetesIngress,omitempty" file:"allowEmpty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *providers) deprecationNotice(logger zerolog.Logger) bool {
|
func (p *providers) deprecationNotice(logger zerolog.Logger) bool {
|
||||||
|
@ -244,7 +243,6 @@ func (p *providers) deprecationNotice(logger zerolog.Logger) bool {
|
||||||
etcdIncompatible := p.ETCD.deprecationNotice(logger)
|
etcdIncompatible := p.ETCD.deprecationNotice(logger)
|
||||||
redisIncompatible := p.Redis.deprecationNotice(logger)
|
redisIncompatible := p.Redis.deprecationNotice(logger)
|
||||||
httpIncompatible := p.HTTP.deprecationNotice(logger)
|
httpIncompatible := p.HTTP.deprecationNotice(logger)
|
||||||
p.KubernetesIngress.deprecationNotice(logger)
|
|
||||||
return incompatible ||
|
return incompatible ||
|
||||||
dockerIncompatible ||
|
dockerIncompatible ||
|
||||||
consulIncompatible ||
|
consulIncompatible ||
|
||||||
|
@ -459,22 +457,6 @@ func (h *http) deprecationNotice(logger zerolog.Logger) bool {
|
||||||
return incompatible
|
return incompatible
|
||||||
}
|
}
|
||||||
|
|
||||||
type ingress struct {
|
|
||||||
DisableIngressClassLookup *bool `json:"disableIngressClassLookup,omitempty" toml:"disableIngressClassLookup,omitempty" yaml:"disableIngressClassLookup,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *ingress) deprecationNotice(logger zerolog.Logger) {
|
|
||||||
if i == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if i.DisableIngressClassLookup != nil {
|
|
||||||
logger.Error().Msg("Kubernetes Ingress provider `disableIngressClassLookup` option has been deprecated in v3.1, and will be removed in the next major version." +
|
|
||||||
"Please use the `disableClusterScopeResources` option instead." +
|
|
||||||
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.1/migration/v3/#ingressclasslookup")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type experimental struct {
|
type experimental struct {
|
||||||
HTTP3 *bool `json:"http3,omitempty" toml:"http3,omitempty" yaml:"http3,omitempty"`
|
HTTP3 *bool `json:"http3,omitempty" toml:"http3,omitempty" yaml:"http3,omitempty"`
|
||||||
KubernetesGateway *bool `json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty"`
|
KubernetesGateway *bool `json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty"`
|
||||||
|
|
|
@ -40,9 +40,8 @@ type Middleware struct {
|
||||||
|
|
||||||
Plugin map[string]PluginConf `json:"plugin,omitempty" toml:"plugin,omitempty" yaml:"plugin,omitempty" export:"true"`
|
Plugin map[string]PluginConf `json:"plugin,omitempty" toml:"plugin,omitempty" yaml:"plugin,omitempty" export:"true"`
|
||||||
|
|
||||||
// Gateway API filter middlewares.
|
// Gateway API HTTPRoute filters middlewares.
|
||||||
RequestHeaderModifier *HeaderModifier `json:"requestHeaderModifier,omitempty" toml:"-" yaml:"-" label:"-" file:"-" kv:"-" export:"true"`
|
RequestHeaderModifier *RequestHeaderModifier `json:"requestHeaderModifier,omitempty" toml:"-" yaml:"-" label:"-" file:"-" kv:"-" export:"true"`
|
||||||
ResponseHeaderModifier *HeaderModifier `json:"responseHeaderModifier,omitempty" toml:"-" yaml:"-" label:"-" file:"-" kv:"-" export:"true"`
|
|
||||||
RequestRedirect *RequestRedirect `json:"requestRedirect,omitempty" toml:"-" yaml:"-" label:"-" file:"-" kv:"-" export:"true"`
|
RequestRedirect *RequestRedirect `json:"requestRedirect,omitempty" toml:"-" yaml:"-" label:"-" file:"-" kv:"-" export:"true"`
|
||||||
URLRewrite *URLRewrite `json:"URLRewrite,omitempty" toml:"-" yaml:"-" label:"-" file:"-" kv:"-" export:"true"`
|
URLRewrite *URLRewrite `json:"URLRewrite,omitempty" toml:"-" yaml:"-" label:"-" file:"-" kv:"-" export:"true"`
|
||||||
}
|
}
|
||||||
|
@ -166,7 +165,8 @@ func (c *CircuitBreaker) SetDefaults() {
|
||||||
// +k8s:deepcopy-gen=true
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// Compress holds the compress middleware configuration.
|
// Compress holds the compress middleware configuration.
|
||||||
// This middleware compresses responses before sending them to the client, using gzip, brotli, or zstd compression.
|
// This middleware compresses responses before sending them to the client, using gzip compression.
|
||||||
|
// More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/compress/
|
||||||
type Compress struct {
|
type Compress struct {
|
||||||
// ExcludedContentTypes defines the list of content types to compare the Content-Type header of the incoming requests and responses before compressing.
|
// ExcludedContentTypes defines the list of content types to compare the Content-Type header of the incoming requests and responses before compressing.
|
||||||
// `application/grpc` is always excluded.
|
// `application/grpc` is always excluded.
|
||||||
|
@ -176,16 +176,10 @@ type Compress struct {
|
||||||
// MinResponseBodyBytes defines the minimum amount of bytes a response body must have to be compressed.
|
// MinResponseBodyBytes defines the minimum amount of bytes a response body must have to be compressed.
|
||||||
// Default: 1024.
|
// Default: 1024.
|
||||||
MinResponseBodyBytes int `json:"minResponseBodyBytes,omitempty" toml:"minResponseBodyBytes,omitempty" yaml:"minResponseBodyBytes,omitempty" export:"true"`
|
MinResponseBodyBytes int `json:"minResponseBodyBytes,omitempty" toml:"minResponseBodyBytes,omitempty" yaml:"minResponseBodyBytes,omitempty" export:"true"`
|
||||||
// Encodings defines the list of supported compression algorithms.
|
|
||||||
Encodings []string `json:"encodings,omitempty" toml:"encodings,omitempty" yaml:"encodings,omitempty" export:"true"`
|
|
||||||
// DefaultEncoding specifies the default encoding if the `Accept-Encoding` header is not in the request or contains a wildcard (`*`).
|
// DefaultEncoding specifies the default encoding if the `Accept-Encoding` header is not in the request or contains a wildcard (`*`).
|
||||||
DefaultEncoding string `json:"defaultEncoding,omitempty" toml:"defaultEncoding,omitempty" yaml:"defaultEncoding,omitempty" export:"true"`
|
DefaultEncoding string `json:"defaultEncoding,omitempty" toml:"defaultEncoding,omitempty" yaml:"defaultEncoding,omitempty" export:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compress) SetDefaults() {
|
|
||||||
c.Encodings = []string{"zstd", "br", "gzip"}
|
|
||||||
}
|
|
||||||
|
|
||||||
// +k8s:deepcopy-gen=true
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// DigestAuth holds the digest auth middleware configuration.
|
// DigestAuth holds the digest auth middleware configuration.
|
||||||
|
@ -695,8 +689,8 @@ type Users []string
|
||||||
|
|
||||||
// +k8s:deepcopy-gen=true
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// HeaderModifier holds the request/response header modifier configuration.
|
// RequestHeaderModifier holds the request header modifier configuration.
|
||||||
type HeaderModifier struct {
|
type RequestHeaderModifier struct {
|
||||||
Set map[string]string `json:"set,omitempty"`
|
Set map[string]string `json:"set,omitempty"`
|
||||||
Add map[string]string `json:"add,omitempty"`
|
Add map[string]string `json:"add,omitempty"`
|
||||||
Remove []string `json:"remove,omitempty"`
|
Remove []string `json:"remove,omitempty"`
|
||||||
|
|
|
@ -158,11 +158,6 @@ func (in *Compress) DeepCopyInto(out *Compress) {
|
||||||
*out = make([]string, len(*in))
|
*out = make([]string, len(*in))
|
||||||
copy(*out, *in)
|
copy(*out, *in)
|
||||||
}
|
}
|
||||||
if in.Encodings != nil {
|
|
||||||
in, out := &in.Encodings, &out.Encodings
|
|
||||||
*out = make([]string, len(*in))
|
|
||||||
copy(*out, *in)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -506,41 +501,6 @@ func (in *HTTPConfiguration) DeepCopy() *HTTPConfiguration {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *HeaderModifier) DeepCopyInto(out *HeaderModifier) {
|
|
||||||
*out = *in
|
|
||||||
if in.Set != nil {
|
|
||||||
in, out := &in.Set, &out.Set
|
|
||||||
*out = make(map[string]string, len(*in))
|
|
||||||
for key, val := range *in {
|
|
||||||
(*out)[key] = val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if in.Add != nil {
|
|
||||||
in, out := &in.Add, &out.Add
|
|
||||||
*out = make(map[string]string, len(*in))
|
|
||||||
for key, val := range *in {
|
|
||||||
(*out)[key] = val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if in.Remove != nil {
|
|
||||||
in, out := &in.Remove, &out.Remove
|
|
||||||
*out = make([]string, len(*in))
|
|
||||||
copy(*out, *in)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HeaderModifier.
|
|
||||||
func (in *HeaderModifier) DeepCopy() *HeaderModifier {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(HeaderModifier)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *Headers) DeepCopyInto(out *Headers) {
|
func (in *Headers) DeepCopyInto(out *Headers) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
@ -901,12 +861,7 @@ func (in *Middleware) DeepCopyInto(out *Middleware) {
|
||||||
}
|
}
|
||||||
if in.RequestHeaderModifier != nil {
|
if in.RequestHeaderModifier != nil {
|
||||||
in, out := &in.RequestHeaderModifier, &out.RequestHeaderModifier
|
in, out := &in.RequestHeaderModifier, &out.RequestHeaderModifier
|
||||||
*out = new(HeaderModifier)
|
*out = new(RequestHeaderModifier)
|
||||||
(*in).DeepCopyInto(*out)
|
|
||||||
}
|
|
||||||
if in.ResponseHeaderModifier != nil {
|
|
||||||
in, out := &in.ResponseHeaderModifier, &out.ResponseHeaderModifier
|
|
||||||
*out = new(HeaderModifier)
|
|
||||||
(*in).DeepCopyInto(*out)
|
(*in).DeepCopyInto(*out)
|
||||||
}
|
}
|
||||||
if in.RequestRedirect != nil {
|
if in.RequestRedirect != nil {
|
||||||
|
@ -1127,6 +1082,41 @@ func (in *ReplacePathRegex) DeepCopy() *ReplacePathRegex {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *RequestHeaderModifier) DeepCopyInto(out *RequestHeaderModifier) {
|
||||||
|
*out = *in
|
||||||
|
if in.Set != nil {
|
||||||
|
in, out := &in.Set, &out.Set
|
||||||
|
*out = make(map[string]string, len(*in))
|
||||||
|
for key, val := range *in {
|
||||||
|
(*out)[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.Add != nil {
|
||||||
|
in, out := &in.Add, &out.Add
|
||||||
|
*out = make(map[string]string, len(*in))
|
||||||
|
for key, val := range *in {
|
||||||
|
(*out)[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.Remove != nil {
|
||||||
|
in, out := &in.Remove, &out.Remove
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RequestHeaderModifier.
|
||||||
|
func (in *RequestHeaderModifier) DeepCopy() *RequestHeaderModifier {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(RequestHeaderModifier)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *RequestRedirect) DeepCopyInto(out *RequestRedirect) {
|
func (in *RequestRedirect) DeepCopyInto(out *RequestRedirect) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
|
|
@ -137,7 +137,6 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||||
"traefik.http.middlewares.Middleware17.stripprefix.prefixes": "foobar, fiibar",
|
"traefik.http.middlewares.Middleware17.stripprefix.prefixes": "foobar, fiibar",
|
||||||
"traefik.http.middlewares.Middleware17.stripprefix.forceslash": "true",
|
"traefik.http.middlewares.Middleware17.stripprefix.forceslash": "true",
|
||||||
"traefik.http.middlewares.Middleware18.stripprefixregex.regex": "foobar, fiibar",
|
"traefik.http.middlewares.Middleware18.stripprefixregex.regex": "foobar, fiibar",
|
||||||
"traefik.http.middlewares.Middleware19.compress.encodings": "foobar, fiibar",
|
|
||||||
"traefik.http.middlewares.Middleware19.compress.minresponsebodybytes": "42",
|
"traefik.http.middlewares.Middleware19.compress.minresponsebodybytes": "42",
|
||||||
"traefik.http.middlewares.Middleware20.plugin.tomato.aaa": "foo1",
|
"traefik.http.middlewares.Middleware20.plugin.tomato.aaa": "foo1",
|
||||||
"traefik.http.middlewares.Middleware20.plugin.tomato.bbb": "foo2",
|
"traefik.http.middlewares.Middleware20.plugin.tomato.bbb": "foo2",
|
||||||
|
@ -494,10 +493,6 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||||
"Middleware19": {
|
"Middleware19": {
|
||||||
Compress: &dynamic.Compress{
|
Compress: &dynamic.Compress{
|
||||||
MinResponseBodyBytes: 42,
|
MinResponseBodyBytes: 42,
|
||||||
Encodings: []string{
|
|
||||||
"foobar",
|
|
||||||
"fiibar",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Middleware2": {
|
"Middleware2": {
|
||||||
|
@ -1014,10 +1009,6 @@ func TestEncodeConfiguration(t *testing.T) {
|
||||||
"Middleware19": {
|
"Middleware19": {
|
||||||
Compress: &dynamic.Compress{
|
Compress: &dynamic.Compress{
|
||||||
MinResponseBodyBytes: 42,
|
MinResponseBodyBytes: 42,
|
||||||
Encodings: []string{
|
|
||||||
"foobar",
|
|
||||||
"fiibar",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Middleware2": {
|
"Middleware2": {
|
||||||
|
@ -1386,7 +1377,6 @@ func TestEncodeConfiguration(t *testing.T) {
|
||||||
"traefik.HTTP.Middlewares.Middleware17.StripPrefix.Prefixes": "foobar, fiibar",
|
"traefik.HTTP.Middlewares.Middleware17.StripPrefix.Prefixes": "foobar, fiibar",
|
||||||
"traefik.HTTP.Middlewares.Middleware17.StripPrefix.ForceSlash": "true",
|
"traefik.HTTP.Middlewares.Middleware17.StripPrefix.ForceSlash": "true",
|
||||||
"traefik.HTTP.Middlewares.Middleware18.StripPrefixRegex.Regex": "foobar, fiibar",
|
"traefik.HTTP.Middlewares.Middleware18.StripPrefixRegex.Regex": "foobar, fiibar",
|
||||||
"traefik.HTTP.Middlewares.Middleware19.Compress.Encodings": "foobar, fiibar",
|
|
||||||
"traefik.HTTP.Middlewares.Middleware19.Compress.MinResponseBodyBytes": "42",
|
"traefik.HTTP.Middlewares.Middleware19.Compress.MinResponseBodyBytes": "42",
|
||||||
"traefik.HTTP.Middlewares.Middleware20.Plugin.tomato.aaa": "foo1",
|
"traefik.HTTP.Middlewares.Middleware20.Plugin.tomato.aaa": "foo1",
|
||||||
"traefik.HTTP.Middlewares.Middleware20.Plugin.tomato.bbb": "foo2",
|
"traefik.HTTP.Middlewares.Middleware20.Plugin.tomato.bbb": "foo2",
|
||||||
|
|
|
@ -22,18 +22,13 @@ type Encoding struct {
|
||||||
Weight *float64
|
Weight *float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCompressionEncoding(acceptEncoding []string, defaultEncoding string, supportedEncodings []string) string {
|
func getCompressionType(acceptEncoding []string, defaultType string) string {
|
||||||
if defaultEncoding == "" {
|
if defaultType == "" {
|
||||||
if slices.Contains(supportedEncodings, brotliName) {
|
// Keeps the pre-existing default inside Traefik.
|
||||||
// Keeps the pre-existing default inside Traefik if brotli is a supported encoding.
|
defaultType = brotliName
|
||||||
defaultEncoding = brotliName
|
|
||||||
} else if len(supportedEncodings) > 0 {
|
|
||||||
// Otherwise use the first supported encoding.
|
|
||||||
defaultEncoding = supportedEncodings[0]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
encodings, hasWeight := parseAcceptEncoding(acceptEncoding, supportedEncodings)
|
encodings, hasWeight := parseAcceptEncoding(acceptEncoding)
|
||||||
|
|
||||||
if hasWeight {
|
if hasWeight {
|
||||||
if len(encodings) == 0 {
|
if len(encodings) == 0 {
|
||||||
|
@ -51,26 +46,26 @@ func getCompressionEncoding(acceptEncoding []string, defaultEncoding string, sup
|
||||||
}
|
}
|
||||||
|
|
||||||
if encoding.Type == wildcardName {
|
if encoding.Type == wildcardName {
|
||||||
return defaultEncoding
|
return defaultType
|
||||||
}
|
}
|
||||||
|
|
||||||
return encoding.Type
|
return encoding.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, dt := range supportedEncodings {
|
for _, dt := range []string{zstdName, brotliName, gzipName} {
|
||||||
if slices.ContainsFunc(encodings, func(e Encoding) bool { return e.Type == dt }) {
|
if slices.ContainsFunc(encodings, func(e Encoding) bool { return e.Type == dt }) {
|
||||||
return dt
|
return dt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if slices.ContainsFunc(encodings, func(e Encoding) bool { return e.Type == wildcardName }) {
|
if slices.ContainsFunc(encodings, func(e Encoding) bool { return e.Type == wildcardName }) {
|
||||||
return defaultEncoding
|
return defaultType
|
||||||
}
|
}
|
||||||
|
|
||||||
return identityName
|
return identityName
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseAcceptEncoding(acceptEncoding, supportedEncodings []string) ([]Encoding, bool) {
|
func parseAcceptEncoding(acceptEncoding []string) ([]Encoding, bool) {
|
||||||
var encodings []Encoding
|
var encodings []Encoding
|
||||||
var hasWeight bool
|
var hasWeight bool
|
||||||
|
|
||||||
|
@ -81,9 +76,10 @@ func parseAcceptEncoding(acceptEncoding, supportedEncodings []string) ([]Encodin
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !slices.Contains(supportedEncodings, parsed[0]) &&
|
switch parsed[0] {
|
||||||
parsed[0] != identityName &&
|
case zstdName, brotliName, gzipName, identityName, wildcardName:
|
||||||
parsed[0] != wildcardName {
|
// supported encoding
|
||||||
|
default:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,100 +6,83 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_getCompressionEncoding(t *testing.T) {
|
func Test_getCompressionType(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
acceptEncoding []string
|
values []string
|
||||||
defaultEncoding string
|
defaultType string
|
||||||
supportedEncodings []string
|
|
||||||
expected string
|
expected string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "br > gzip (no weight)",
|
desc: "br > gzip (no weight)",
|
||||||
acceptEncoding: []string{"gzip, br"},
|
values: []string{"gzip, br"},
|
||||||
expected: brotliName,
|
expected: brotliName,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "zstd > br > gzip (no weight)",
|
desc: "zstd > br > gzip (no weight)",
|
||||||
acceptEncoding: []string{"zstd, gzip, br"},
|
values: []string{"zstd, gzip, br"},
|
||||||
expected: zstdName,
|
expected: zstdName,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "known compression encoding (no weight)",
|
desc: "known compression type (no weight)",
|
||||||
acceptEncoding: []string{"compress, gzip"},
|
values: []string{"compress, gzip"},
|
||||||
expected: gzipName,
|
expected: gzipName,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "unknown compression encoding (no weight), no encoding",
|
desc: "unknown compression type (no weight), no encoding",
|
||||||
acceptEncoding: []string{"compress, rar"},
|
values: []string{"compress, rar"},
|
||||||
expected: identityName,
|
expected: identityName,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "wildcard return the default compression encoding",
|
desc: "wildcard return the default compression type",
|
||||||
acceptEncoding: []string{"*"},
|
values: []string{"*"},
|
||||||
expected: brotliName,
|
expected: brotliName,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "wildcard return the custom default compression encoding",
|
desc: "wildcard return the custom default compression type",
|
||||||
acceptEncoding: []string{"*"},
|
values: []string{"*"},
|
||||||
defaultEncoding: "foo",
|
defaultType: "foo",
|
||||||
expected: "foo",
|
expected: "foo",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "follows weight",
|
desc: "follows weight",
|
||||||
acceptEncoding: []string{"br;q=0.8, gzip;q=1.0, *;q=0.1"},
|
values: []string{"br;q=0.8, gzip;q=1.0, *;q=0.1"},
|
||||||
expected: gzipName,
|
expected: gzipName,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "ignore unknown compression encoding",
|
desc: "ignore unknown compression type",
|
||||||
acceptEncoding: []string{"compress;q=1.0, gzip;q=0.5"},
|
values: []string{"compress;q=1.0, gzip;q=0.5"},
|
||||||
expected: gzipName,
|
expected: gzipName,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "fallback on non-zero compression encoding",
|
desc: "fallback on non-zero compression type",
|
||||||
acceptEncoding: []string{"compress;q=1.0, gzip, identity;q=0"},
|
values: []string{"compress;q=1.0, gzip, identity;q=0"},
|
||||||
expected: gzipName,
|
expected: gzipName,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "not acceptable (identity)",
|
desc: "not acceptable (identity)",
|
||||||
acceptEncoding: []string{"compress;q=1.0, identity;q=0"},
|
values: []string{"compress;q=1.0, identity;q=0"},
|
||||||
expected: notAcceptable,
|
expected: notAcceptable,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "not acceptable (wildcard)",
|
desc: "not acceptable (wildcard)",
|
||||||
acceptEncoding: []string{"compress;q=1.0, *;q=0"},
|
values: []string{"compress;q=1.0, *;q=0"},
|
||||||
expected: notAcceptable,
|
expected: notAcceptable,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "non-zero is higher than 0",
|
desc: "non-zero is higher than 0",
|
||||||
acceptEncoding: []string{"gzip, *;q=0"},
|
values: []string{"gzip, *;q=0"},
|
||||||
expected: gzipName,
|
expected: gzipName,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
desc: "zstd forbidden, brotli first",
|
|
||||||
acceptEncoding: []string{"zstd, gzip, br"},
|
|
||||||
supportedEncodings: []string{brotliName, gzipName},
|
|
||||||
expected: brotliName,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "follows weight, ignores forbidden encoding",
|
|
||||||
acceptEncoding: []string{"br;q=0.8, gzip;q=1.0, *;q=0.1"},
|
|
||||||
supportedEncodings: []string{zstdName, brotliName},
|
|
||||||
expected: brotliName,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
if test.supportedEncodings == nil {
|
encodingType := getCompressionType(test.values, test.defaultType)
|
||||||
test.supportedEncodings = defaultSupportedEncodings
|
|
||||||
}
|
|
||||||
|
|
||||||
encoding := getCompressionEncoding(test.acceptEncoding, test.defaultEncoding, test.supportedEncodings)
|
assert.Equal(t, test.expected, encodingType)
|
||||||
|
|
||||||
assert.Equal(t, test.expected, encoding)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,7 +91,6 @@ func Test_parseAcceptEncoding(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
values []string
|
values []string
|
||||||
supportedEncodings []string
|
|
||||||
expected []Encoding
|
expected []Encoding
|
||||||
assertWeight assert.BoolAssertionFunc
|
assertWeight assert.BoolAssertionFunc
|
||||||
}{
|
}{
|
||||||
|
@ -123,17 +105,6 @@ func Test_parseAcceptEncoding(t *testing.T) {
|
||||||
},
|
},
|
||||||
assertWeight: assert.True,
|
assertWeight: assert.True,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
desc: "weight with supported encodings",
|
|
||||||
values: []string{"br;q=1.0, zstd;q=0.9, gzip;q=0.8, *;q=0.1"},
|
|
||||||
supportedEncodings: []string{brotliName, gzipName},
|
|
||||||
expected: []Encoding{
|
|
||||||
{Type: brotliName, Weight: ptr[float64](1)},
|
|
||||||
{Type: gzipName, Weight: ptr(0.8)},
|
|
||||||
{Type: wildcardName, Weight: ptr(0.1)},
|
|
||||||
},
|
|
||||||
assertWeight: assert.True,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
desc: "mixed",
|
desc: "mixed",
|
||||||
values: []string{"zstd,gzip, br;q=1.0, *;q=0"},
|
values: []string{"zstd,gzip, br;q=1.0, *;q=0"},
|
||||||
|
@ -145,16 +116,6 @@ func Test_parseAcceptEncoding(t *testing.T) {
|
||||||
},
|
},
|
||||||
assertWeight: assert.True,
|
assertWeight: assert.True,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
desc: "mixed with supported encodings",
|
|
||||||
values: []string{"zstd,gzip, br;q=1.0, *;q=0"},
|
|
||||||
supportedEncodings: []string{zstdName},
|
|
||||||
expected: []Encoding{
|
|
||||||
{Type: zstdName},
|
|
||||||
{Type: wildcardName, Weight: ptr[float64](0)},
|
|
||||||
},
|
|
||||||
assertWeight: assert.True,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
desc: "no weight",
|
desc: "no weight",
|
||||||
values: []string{"zstd, gzip, br, *"},
|
values: []string{"zstd, gzip, br, *"},
|
||||||
|
@ -166,16 +127,6 @@ func Test_parseAcceptEncoding(t *testing.T) {
|
||||||
},
|
},
|
||||||
assertWeight: assert.False,
|
assertWeight: assert.False,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
desc: "no weight with supported encodings",
|
|
||||||
values: []string{"zstd, gzip, br, *"},
|
|
||||||
supportedEncodings: []string{"gzip"},
|
|
||||||
expected: []Encoding{
|
|
||||||
{Type: gzipName},
|
|
||||||
{Type: wildcardName},
|
|
||||||
},
|
|
||||||
assertWeight: assert.False,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
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"},
|
||||||
|
@ -186,27 +137,13 @@ func Test_parseAcceptEncoding(t *testing.T) {
|
||||||
},
|
},
|
||||||
assertWeight: assert.True,
|
assertWeight: assert.True,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
desc: "weight and identity",
|
|
||||||
values: []string{"gzip;q=1.0, identity; q=0.5, *;q=0"},
|
|
||||||
supportedEncodings: []string{"br"},
|
|
||||||
expected: []Encoding{
|
|
||||||
{Type: identityName, Weight: ptr(0.5)},
|
|
||||||
{Type: wildcardName, Weight: ptr[float64](0)},
|
|
||||||
},
|
|
||||||
assertWeight: assert.True,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
if test.supportedEncodings == nil {
|
aes, hasWeight := parseAcceptEncoding(test.values)
|
||||||
test.supportedEncodings = defaultSupportedEncodings
|
|
||||||
}
|
|
||||||
|
|
||||||
aes, hasWeight := parseAcceptEncoding(test.values, test.supportedEncodings)
|
|
||||||
|
|
||||||
assert.Equal(t, test.expected, aes)
|
assert.Equal(t, test.expected, aes)
|
||||||
test.assertWeight(t, hasWeight)
|
test.assertWeight(t, hasWeight)
|
||||||
|
|
|
@ -16,11 +16,9 @@ import (
|
||||||
|
|
||||||
const typeName = "Compress"
|
const typeName = "Compress"
|
||||||
|
|
||||||
// defaultMinSize is the default minimum size (in bytes) required to enable compression.
|
// DefaultMinSize is the default minimum size (in bytes) required to enable compression.
|
||||||
// See https://github.com/klauspost/compress/blob/9559b037e79ad673c71f6ef7c732c00949014cd2/gzhttp/compress.go#L47.
|
// See https://github.com/klauspost/compress/blob/9559b037e79ad673c71f6ef7c732c00949014cd2/gzhttp/compress.go#L47.
|
||||||
const defaultMinSize = 1024
|
const DefaultMinSize = 1024
|
||||||
|
|
||||||
var defaultSupportedEncodings = []string{zstdName, brotliName, gzipName}
|
|
||||||
|
|
||||||
// Compress is a middleware that allows to compress the response.
|
// Compress is a middleware that allows to compress the response.
|
||||||
type compress struct {
|
type compress struct {
|
||||||
|
@ -29,7 +27,6 @@ type compress struct {
|
||||||
excludes []string
|
excludes []string
|
||||||
includes []string
|
includes []string
|
||||||
minSize int
|
minSize int
|
||||||
encodings []string
|
|
||||||
defaultEncoding string
|
defaultEncoding string
|
||||||
|
|
||||||
brotliHandler http.Handler
|
brotliHandler http.Handler
|
||||||
|
@ -65,30 +62,17 @@ func New(ctx context.Context, next http.Handler, conf dynamic.Compress, name str
|
||||||
includes = append(includes, mediaType)
|
includes = append(includes, mediaType)
|
||||||
}
|
}
|
||||||
|
|
||||||
minSize := defaultMinSize
|
minSize := DefaultMinSize
|
||||||
if conf.MinResponseBodyBytes > 0 {
|
if conf.MinResponseBodyBytes > 0 {
|
||||||
minSize = conf.MinResponseBodyBytes
|
minSize = conf.MinResponseBodyBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(conf.Encodings) == 0 {
|
|
||||||
return nil, errors.New("at least one encoding must be specified")
|
|
||||||
}
|
|
||||||
for _, encoding := range conf.Encodings {
|
|
||||||
if !slices.Contains(defaultSupportedEncodings, encoding) {
|
|
||||||
return nil, fmt.Errorf("unsupported encoding: %s", encoding)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if conf.DefaultEncoding != "" && !slices.Contains(conf.Encodings, conf.DefaultEncoding) {
|
|
||||||
return nil, fmt.Errorf("unsupported default encoding: %s", conf.DefaultEncoding)
|
|
||||||
}
|
|
||||||
|
|
||||||
c := &compress{
|
c := &compress{
|
||||||
next: next,
|
next: next,
|
||||||
name: name,
|
name: name,
|
||||||
excludes: excludes,
|
excludes: excludes,
|
||||||
includes: includes,
|
includes: includes,
|
||||||
minSize: minSize,
|
minSize: minSize,
|
||||||
encodings: conf.Encodings,
|
|
||||||
defaultEncoding: conf.DefaultEncoding,
|
defaultEncoding: conf.DefaultEncoding,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +131,7 @@ func (c *compress) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.chooseHandler(getCompressionEncoding(acceptEncoding, c.defaultEncoding, c.encodings), rw, req)
|
c.chooseHandler(getCompressionType(acceptEncoding, c.defaultEncoding), rw, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *compress) chooseHandler(typ string, rw http.ResponseWriter, req *http.Request) {
|
func (c *compress) chooseHandler(typ string, rw http.ResponseWriter, req *http.Request) {
|
||||||
|
|
|
@ -102,11 +102,7 @@ func TestNegotiation(t *testing.T) {
|
||||||
next := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
next := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
_, _ = rw.Write(generateBytes(10))
|
_, _ = rw.Write(generateBytes(10))
|
||||||
})
|
})
|
||||||
cfg := dynamic.Compress{
|
handler, err := New(context.Background(), next, dynamic.Compress{MinResponseBodyBytes: 1}, "testing")
|
||||||
MinResponseBodyBytes: 1,
|
|
||||||
Encodings: defaultSupportedEncodings,
|
|
||||||
}
|
|
||||||
handler, err := New(context.Background(), next, cfg, "testing")
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
|
@ -127,7 +123,7 @@ func TestShouldCompressWhenNoContentEncodingHeader(t *testing.T) {
|
||||||
_, err := rw.Write(baseBody)
|
_, err := rw.Write(baseBody)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
})
|
})
|
||||||
handler, err := New(context.Background(), next, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing")
|
handler, err := New(context.Background(), next, dynamic.Compress{}, "testing")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
|
@ -157,7 +153,7 @@ func TestShouldNotCompressWhenContentEncodingHeader(t *testing.T) {
|
||||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
handler, err := New(context.Background(), next, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing")
|
handler, err := New(context.Background(), next, dynamic.Compress{}, "testing")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
|
@ -179,7 +175,7 @@ func TestShouldNotCompressWhenNoAcceptEncodingHeader(t *testing.T) {
|
||||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
handler, err := New(context.Background(), next, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing")
|
handler, err := New(context.Background(), next, dynamic.Compress{}, "testing")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
|
@ -206,7 +202,7 @@ func TestShouldNotCompressWhenIdentityAcceptEncodingHeader(t *testing.T) {
|
||||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
handler, err := New(context.Background(), next, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing")
|
handler, err := New(context.Background(), next, dynamic.Compress{}, "testing")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
|
@ -233,7 +229,7 @@ func TestShouldNotCompressWhenEmptyAcceptEncodingHeader(t *testing.T) {
|
||||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
handler, err := New(context.Background(), next, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing")
|
handler, err := New(context.Background(), next, dynamic.Compress{}, "testing")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
|
@ -255,7 +251,7 @@ func TestShouldNotCompressHeadRequest(t *testing.T) {
|
||||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
handler, err := New(context.Background(), next, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing")
|
handler, err := New(context.Background(), next, dynamic.Compress{}, "testing")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
|
@ -278,7 +274,6 @@ func TestShouldNotCompressWhenSpecificContentType(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Exclude Request Content-Type",
|
desc: "Exclude Request Content-Type",
|
||||||
conf: dynamic.Compress{
|
conf: dynamic.Compress{
|
||||||
Encodings: defaultSupportedEncodings,
|
|
||||||
ExcludedContentTypes: []string{"text/event-stream"},
|
ExcludedContentTypes: []string{"text/event-stream"},
|
||||||
},
|
},
|
||||||
reqContentType: "text/event-stream",
|
reqContentType: "text/event-stream",
|
||||||
|
@ -286,7 +281,6 @@ func TestShouldNotCompressWhenSpecificContentType(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Exclude Response Content-Type",
|
desc: "Exclude Response Content-Type",
|
||||||
conf: dynamic.Compress{
|
conf: dynamic.Compress{
|
||||||
Encodings: defaultSupportedEncodings,
|
|
||||||
ExcludedContentTypes: []string{"text/event-stream"},
|
ExcludedContentTypes: []string{"text/event-stream"},
|
||||||
},
|
},
|
||||||
respContentType: "text/event-stream",
|
respContentType: "text/event-stream",
|
||||||
|
@ -294,7 +288,6 @@ func TestShouldNotCompressWhenSpecificContentType(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Include Response Content-Type",
|
desc: "Include Response Content-Type",
|
||||||
conf: dynamic.Compress{
|
conf: dynamic.Compress{
|
||||||
Encodings: defaultSupportedEncodings,
|
|
||||||
IncludedContentTypes: []string{"text/plain"},
|
IncludedContentTypes: []string{"text/plain"},
|
||||||
},
|
},
|
||||||
respContentType: "text/html",
|
respContentType: "text/html",
|
||||||
|
@ -302,7 +295,6 @@ func TestShouldNotCompressWhenSpecificContentType(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ignoring application/grpc with exclude option",
|
desc: "Ignoring application/grpc with exclude option",
|
||||||
conf: dynamic.Compress{
|
conf: dynamic.Compress{
|
||||||
Encodings: defaultSupportedEncodings,
|
|
||||||
ExcludedContentTypes: []string{"application/json"},
|
ExcludedContentTypes: []string{"application/json"},
|
||||||
},
|
},
|
||||||
reqContentType: "application/grpc",
|
reqContentType: "application/grpc",
|
||||||
|
@ -310,16 +302,13 @@ func TestShouldNotCompressWhenSpecificContentType(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ignoring application/grpc with include option",
|
desc: "Ignoring application/grpc with include option",
|
||||||
conf: dynamic.Compress{
|
conf: dynamic.Compress{
|
||||||
Encodings: defaultSupportedEncodings,
|
|
||||||
IncludedContentTypes: []string{"application/json"},
|
IncludedContentTypes: []string{"application/json"},
|
||||||
},
|
},
|
||||||
reqContentType: "application/grpc",
|
reqContentType: "application/grpc",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Ignoring application/grpc with no option",
|
desc: "Ignoring application/grpc with no option",
|
||||||
conf: dynamic.Compress{
|
conf: dynamic.Compress{},
|
||||||
Encodings: defaultSupportedEncodings,
|
|
||||||
},
|
|
||||||
reqContentType: "application/grpc",
|
reqContentType: "application/grpc",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -369,7 +358,6 @@ func TestShouldCompressWhenSpecificContentType(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Include Response Content-Type",
|
desc: "Include Response Content-Type",
|
||||||
conf: dynamic.Compress{
|
conf: dynamic.Compress{
|
||||||
Encodings: defaultSupportedEncodings,
|
|
||||||
IncludedContentTypes: []string{"text/html"},
|
IncludedContentTypes: []string{"text/html"},
|
||||||
},
|
},
|
||||||
respContentType: "text/html",
|
respContentType: "text/html",
|
||||||
|
@ -441,7 +429,7 @@ func TestIntegrationShouldNotCompress(t *testing.T) {
|
||||||
|
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
compress, err := New(context.Background(), test.handler, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing")
|
compress, err := New(context.Background(), test.handler, dynamic.Compress{}, "testing")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
ts := httptest.NewServer(compress)
|
ts := httptest.NewServer(compress)
|
||||||
|
@ -476,7 +464,7 @@ func TestShouldWriteHeaderWhenFlush(t *testing.T) {
|
||||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
handler, err := New(context.Background(), next, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing")
|
handler, err := New(context.Background(), next, dynamic.Compress{}, "testing")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
ts := httptest.NewServer(handler)
|
ts := httptest.NewServer(handler)
|
||||||
|
@ -527,7 +515,7 @@ func TestIntegrationShouldCompress(t *testing.T) {
|
||||||
|
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
compress, err := New(context.Background(), test.handler, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing")
|
compress, err := New(context.Background(), test.handler, dynamic.Compress{}, "testing")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
ts := httptest.NewServer(compress)
|
ts := httptest.NewServer(compress)
|
||||||
|
@ -583,11 +571,8 @@ func TestMinResponseBodyBytes(t *testing.T) {
|
||||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
cfg := dynamic.Compress{
|
|
||||||
MinResponseBodyBytes: test.minResponseBodyBytes,
|
handler, err := New(context.Background(), next, dynamic.Compress{MinResponseBodyBytes: test.minResponseBodyBytes}, "testing")
|
||||||
Encodings: defaultSupportedEncodings,
|
|
||||||
}
|
|
||||||
handler, err := New(context.Background(), next, cfg, "testing")
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
|
@ -622,11 +607,8 @@ func Test1xxResponses(t *testing.T) {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
cfg := dynamic.Compress{
|
|
||||||
MinResponseBodyBytes: 1024,
|
compress, err := New(context.Background(), next, dynamic.Compress{MinResponseBodyBytes: 1024}, "testing")
|
||||||
Encodings: defaultSupportedEncodings,
|
|
||||||
}
|
|
||||||
compress, err := New(context.Background(), next, cfg, "testing")
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
server := httptest.NewServer(compress)
|
server := httptest.NewServer(compress)
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
const requestHeaderModifierTypeName = "RequestHeaderModifier"
|
const typeName = "RequestHeaderModifier"
|
||||||
|
|
||||||
// requestHeaderModifier is a middleware used to modify the headers of an HTTP request.
|
// requestHeaderModifier is a middleware used to modify the headers of an HTTP request.
|
||||||
type requestHeaderModifier struct {
|
type requestHeaderModifier struct {
|
||||||
|
@ -22,8 +22,8 @@ type requestHeaderModifier struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRequestHeaderModifier creates a new request header modifier middleware.
|
// NewRequestHeaderModifier creates a new request header modifier middleware.
|
||||||
func NewRequestHeaderModifier(ctx context.Context, next http.Handler, config dynamic.HeaderModifier, name string) http.Handler {
|
func NewRequestHeaderModifier(ctx context.Context, next http.Handler, config dynamic.RequestHeaderModifier, name string) http.Handler {
|
||||||
logger := middlewares.GetLogger(ctx, name, requestHeaderModifierTypeName)
|
logger := middlewares.GetLogger(ctx, name, typeName)
|
||||||
logger.Debug().Msg("Creating middleware")
|
logger.Debug().Msg("Creating middleware")
|
||||||
|
|
||||||
return &requestHeaderModifier{
|
return &requestHeaderModifier{
|
||||||
|
@ -36,7 +36,7 @@ func NewRequestHeaderModifier(ctx context.Context, next http.Handler, config dyn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *requestHeaderModifier) GetTracingInformation() (string, string, trace.SpanKind) {
|
func (r *requestHeaderModifier) GetTracingInformation() (string, string, trace.SpanKind) {
|
||||||
return r.name, requestHeaderModifierTypeName, trace.SpanKindUnspecified
|
return r.name, typeName, trace.SpanKindUnspecified
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *requestHeaderModifier) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
func (r *requestHeaderModifier) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
|
|
@ -14,25 +14,25 @@ import (
|
||||||
func TestRequestHeaderModifier(t *testing.T) {
|
func TestRequestHeaderModifier(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
config dynamic.HeaderModifier
|
config dynamic.RequestHeaderModifier
|
||||||
requestHeaders http.Header
|
requestHeaders http.Header
|
||||||
expectedHeaders http.Header
|
expectedHeaders http.Header
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "no config",
|
desc: "no config",
|
||||||
config: dynamic.HeaderModifier{},
|
config: dynamic.RequestHeaderModifier{},
|
||||||
expectedHeaders: map[string][]string{},
|
expectedHeaders: map[string][]string{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "set header",
|
desc: "set header",
|
||||||
config: dynamic.HeaderModifier{
|
config: dynamic.RequestHeaderModifier{
|
||||||
Set: map[string]string{"Foo": "Bar"},
|
Set: map[string]string{"Foo": "Bar"},
|
||||||
},
|
},
|
||||||
expectedHeaders: map[string][]string{"Foo": {"Bar"}},
|
expectedHeaders: map[string][]string{"Foo": {"Bar"}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "set header with existing headers",
|
desc: "set header with existing headers",
|
||||||
config: dynamic.HeaderModifier{
|
config: dynamic.RequestHeaderModifier{
|
||||||
Set: map[string]string{"Foo": "Bar"},
|
Set: map[string]string{"Foo": "Bar"},
|
||||||
},
|
},
|
||||||
requestHeaders: map[string][]string{"Foo": {"Baz"}, "Bar": {"Foo"}},
|
requestHeaders: map[string][]string{"Foo": {"Baz"}, "Bar": {"Foo"}},
|
||||||
|
@ -40,7 +40,7 @@ func TestRequestHeaderModifier(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "set multiple headers with existing headers",
|
desc: "set multiple headers with existing headers",
|
||||||
config: dynamic.HeaderModifier{
|
config: dynamic.RequestHeaderModifier{
|
||||||
Set: map[string]string{"Foo": "Bar", "Bar": "Foo"},
|
Set: map[string]string{"Foo": "Bar", "Bar": "Foo"},
|
||||||
},
|
},
|
||||||
requestHeaders: map[string][]string{"Foo": {"Baz"}, "Bar": {"Foobar"}},
|
requestHeaders: map[string][]string{"Foo": {"Baz"}, "Bar": {"Foobar"}},
|
||||||
|
@ -48,14 +48,14 @@ func TestRequestHeaderModifier(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "add header",
|
desc: "add header",
|
||||||
config: dynamic.HeaderModifier{
|
config: dynamic.RequestHeaderModifier{
|
||||||
Add: map[string]string{"Foo": "Bar"},
|
Add: map[string]string{"Foo": "Bar"},
|
||||||
},
|
},
|
||||||
expectedHeaders: map[string][]string{"Foo": {"Bar"}},
|
expectedHeaders: map[string][]string{"Foo": {"Bar"}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "add header with existing headers",
|
desc: "add header with existing headers",
|
||||||
config: dynamic.HeaderModifier{
|
config: dynamic.RequestHeaderModifier{
|
||||||
Add: map[string]string{"Foo": "Bar"},
|
Add: map[string]string{"Foo": "Bar"},
|
||||||
},
|
},
|
||||||
requestHeaders: map[string][]string{"Foo": {"Baz"}, "Bar": {"Foo"}},
|
requestHeaders: map[string][]string{"Foo": {"Baz"}, "Bar": {"Foo"}},
|
||||||
|
@ -63,7 +63,7 @@ func TestRequestHeaderModifier(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "add multiple headers with existing headers",
|
desc: "add multiple headers with existing headers",
|
||||||
config: dynamic.HeaderModifier{
|
config: dynamic.RequestHeaderModifier{
|
||||||
Add: map[string]string{"Foo": "Bar", "Bar": "Foo"},
|
Add: map[string]string{"Foo": "Bar", "Bar": "Foo"},
|
||||||
},
|
},
|
||||||
requestHeaders: map[string][]string{"Foo": {"Baz"}, "Bar": {"Foobar"}},
|
requestHeaders: map[string][]string{"Foo": {"Baz"}, "Bar": {"Foobar"}},
|
||||||
|
@ -71,14 +71,14 @@ func TestRequestHeaderModifier(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "remove header",
|
desc: "remove header",
|
||||||
config: dynamic.HeaderModifier{
|
config: dynamic.RequestHeaderModifier{
|
||||||
Remove: []string{"Foo"},
|
Remove: []string{"Foo"},
|
||||||
},
|
},
|
||||||
expectedHeaders: map[string][]string{},
|
expectedHeaders: map[string][]string{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "remove header with existing headers",
|
desc: "remove header with existing headers",
|
||||||
config: dynamic.HeaderModifier{
|
config: dynamic.RequestHeaderModifier{
|
||||||
Remove: []string{"Foo"},
|
Remove: []string{"Foo"},
|
||||||
},
|
},
|
||||||
requestHeaders: map[string][]string{"Foo": {"Baz"}, "Bar": {"Foo"}},
|
requestHeaders: map[string][]string{"Foo": {"Baz"}, "Bar": {"Foo"}},
|
||||||
|
@ -86,7 +86,7 @@ func TestRequestHeaderModifier(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "remove multiple headers with existing headers",
|
desc: "remove multiple headers with existing headers",
|
||||||
config: dynamic.HeaderModifier{
|
config: dynamic.RequestHeaderModifier{
|
||||||
Remove: []string{"Foo", "Bar"},
|
Remove: []string{"Foo", "Bar"},
|
||||||
},
|
},
|
||||||
requestHeaders: map[string][]string{"Foo": {"Bar"}, "Bar": {"Foo"}, "Baz": {"Bar"}},
|
requestHeaders: map[string][]string{"Foo": {"Bar"}, "Bar": {"Foo"}, "Baz": {"Bar"}},
|
||||||
|
@ -106,11 +106,11 @@ func TestRequestHeaderModifier(t *testing.T) {
|
||||||
handler := NewRequestHeaderModifier(context.Background(), next, test.config, "foo-request-header-modifier")
|
handler := NewRequestHeaderModifier(context.Background(), next, test.config, "foo-request-header-modifier")
|
||||||
|
|
||||||
req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost", nil)
|
req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost", nil)
|
||||||
for h, v := range test.requestHeaders {
|
if test.requestHeaders != nil {
|
||||||
req.Header[h] = v
|
req.Header = test.requestHeaders
|
||||||
}
|
}
|
||||||
resp := httptest.NewRecorder()
|
|
||||||
|
|
||||||
|
resp := httptest.NewRecorder()
|
||||||
handler.ServeHTTP(resp, req)
|
handler.ServeHTTP(resp, req)
|
||||||
|
|
||||||
assert.Equal(t, test.expectedHeaders, gotHeaders)
|
assert.Equal(t, test.expectedHeaders, gotHeaders)
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
package headermodifier
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
|
||||||
"github.com/traefik/traefik/v3/pkg/middlewares"
|
|
||||||
"go.opentelemetry.io/otel/trace"
|
|
||||||
)
|
|
||||||
|
|
||||||
const responseHeaderModifierTypeName = "ResponseHeaderModifier"
|
|
||||||
|
|
||||||
// requestHeaderModifier is a middleware used to modify the headers of an HTTP response.
|
|
||||||
type responseHeaderModifier struct {
|
|
||||||
next http.Handler
|
|
||||||
name string
|
|
||||||
|
|
||||||
set map[string]string
|
|
||||||
add map[string]string
|
|
||||||
remove []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewResponseHeaderModifier creates a new response header modifier middleware.
|
|
||||||
func NewResponseHeaderModifier(ctx context.Context, next http.Handler, config dynamic.HeaderModifier, name string) http.Handler {
|
|
||||||
logger := middlewares.GetLogger(ctx, name, responseHeaderModifierTypeName)
|
|
||||||
logger.Debug().Msg("Creating middleware")
|
|
||||||
|
|
||||||
return &responseHeaderModifier{
|
|
||||||
next: next,
|
|
||||||
name: name,
|
|
||||||
set: config.Set,
|
|
||||||
add: config.Add,
|
|
||||||
remove: config.Remove,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *responseHeaderModifier) GetTracingInformation() (string, string, trace.SpanKind) {
|
|
||||||
return r.name, responseHeaderModifierTypeName, trace.SpanKindUnspecified
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *responseHeaderModifier) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|
||||||
r.next.ServeHTTP(middlewares.NewResponseModifier(rw, req, r.modifyResponseHeaders), req)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *responseHeaderModifier) modifyResponseHeaders(res *http.Response) error {
|
|
||||||
for headerName, headerValue := range r.set {
|
|
||||||
res.Header.Set(headerName, headerValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
for headerName, headerValue := range r.add {
|
|
||||||
res.Header.Add(headerName, headerValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, headerName := range r.remove {
|
|
||||||
res.Header.Del(headerName)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,121 +0,0 @@
|
||||||
package headermodifier
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
|
||||||
"github.com/traefik/traefik/v3/pkg/testhelpers"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestResponseHeaderModifier(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
config dynamic.HeaderModifier
|
|
||||||
responseHeaders http.Header
|
|
||||||
expectedHeaders http.Header
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "no config",
|
|
||||||
config: dynamic.HeaderModifier{},
|
|
||||||
expectedHeaders: map[string][]string{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "set header",
|
|
||||||
config: dynamic.HeaderModifier{
|
|
||||||
Set: map[string]string{"Foo": "Bar"},
|
|
||||||
},
|
|
||||||
expectedHeaders: map[string][]string{"Foo": {"Bar"}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "set header with existing headers",
|
|
||||||
config: dynamic.HeaderModifier{
|
|
||||||
Set: map[string]string{"Foo": "Bar"},
|
|
||||||
},
|
|
||||||
responseHeaders: map[string][]string{"Foo": {"Baz"}, "Bar": {"Foo"}},
|
|
||||||
expectedHeaders: map[string][]string{"Foo": {"Bar"}, "Bar": {"Foo"}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "set multiple headers with existing headers",
|
|
||||||
config: dynamic.HeaderModifier{
|
|
||||||
Set: map[string]string{"Foo": "Bar", "Bar": "Foo"},
|
|
||||||
},
|
|
||||||
responseHeaders: map[string][]string{"Foo": {"Baz"}, "Bar": {"Foobar"}},
|
|
||||||
expectedHeaders: map[string][]string{"Foo": {"Bar"}, "Bar": {"Foo"}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "add header",
|
|
||||||
config: dynamic.HeaderModifier{
|
|
||||||
Add: map[string]string{"Foo": "Bar"},
|
|
||||||
},
|
|
||||||
expectedHeaders: map[string][]string{"Foo": {"Bar"}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "add header with existing headers",
|
|
||||||
config: dynamic.HeaderModifier{
|
|
||||||
Add: map[string]string{"Foo": "Bar"},
|
|
||||||
},
|
|
||||||
responseHeaders: map[string][]string{"Foo": {"Baz"}, "Bar": {"Foo"}},
|
|
||||||
expectedHeaders: map[string][]string{"Foo": {"Baz", "Bar"}, "Bar": {"Foo"}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "add multiple headers with existing headers",
|
|
||||||
config: dynamic.HeaderModifier{
|
|
||||||
Add: map[string]string{"Foo": "Bar", "Bar": "Foo"},
|
|
||||||
},
|
|
||||||
responseHeaders: map[string][]string{"Foo": {"Baz"}, "Bar": {"Foobar"}},
|
|
||||||
expectedHeaders: map[string][]string{"Foo": {"Baz", "Bar"}, "Bar": {"Foobar", "Foo"}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "remove header",
|
|
||||||
config: dynamic.HeaderModifier{
|
|
||||||
Remove: []string{"Foo"},
|
|
||||||
},
|
|
||||||
expectedHeaders: map[string][]string{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "remove header with existing headers",
|
|
||||||
config: dynamic.HeaderModifier{
|
|
||||||
Remove: []string{"Foo"},
|
|
||||||
},
|
|
||||||
responseHeaders: map[string][]string{"Foo": {"Baz"}, "Bar": {"Foo"}},
|
|
||||||
expectedHeaders: map[string][]string{"Bar": {"Foo"}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "remove multiple headers with existing headers",
|
|
||||||
config: dynamic.HeaderModifier{
|
|
||||||
Remove: []string{"Foo", "Bar"},
|
|
||||||
},
|
|
||||||
responseHeaders: map[string][]string{"Foo": {"Bar"}, "Bar": {"Foo"}, "Baz": {"Bar"}},
|
|
||||||
expectedHeaders: map[string][]string{"Baz": {"Bar"}},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var nextCallCount int
|
|
||||||
next := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
|
||||||
nextCallCount++
|
|
||||||
rw.WriteHeader(http.StatusOK)
|
|
||||||
})
|
|
||||||
|
|
||||||
handler := NewResponseHeaderModifier(context.Background(), next, test.config, "foo-response-header-modifier")
|
|
||||||
|
|
||||||
req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost", nil)
|
|
||||||
resp := httptest.NewRecorder()
|
|
||||||
for k, v := range test.responseHeaders {
|
|
||||||
resp.Header()[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
handler.ServeHTTP(resp, req)
|
|
||||||
|
|
||||||
assert.Equal(t, 1, nextCallCount)
|
|
||||||
assert.Equal(t, test.expectedHeaders, resp.Header())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -13,7 +13,9 @@ import (
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
const typeName = "RequestRedirect"
|
const (
|
||||||
|
typeName = "RequestRedirect"
|
||||||
|
)
|
||||||
|
|
||||||
type redirect struct {
|
type redirect struct {
|
||||||
name string
|
name string
|
||||||
|
|
|
@ -696,8 +696,6 @@ func getCertificateRenewDurations(certificatesDuration int) (time.Duration, time
|
||||||
return 4 * 30 * 24 * time.Hour, 7 * 24 * time.Hour // 4 month, 1 week
|
return 4 * 30 * 24 * time.Hour, 7 * 24 * time.Hour // 4 month, 1 week
|
||||||
case certificatesDuration >= 3*30*24: // >= 90 days
|
case certificatesDuration >= 3*30*24: // >= 90 days
|
||||||
return 30 * 24 * time.Hour, 24 * time.Hour // 30 days, 1 day
|
return 30 * 24 * time.Hour, 24 * time.Hour // 30 days, 1 day
|
||||||
case certificatesDuration >= 30*24: // >= 30 days
|
|
||||||
return 10 * 24 * time.Hour, 12 * time.Hour // 10 days, 12 hours
|
|
||||||
case certificatesDuration >= 7*24: // >= 7 days
|
case certificatesDuration >= 7*24: // >= 7 days
|
||||||
return 24 * time.Hour, time.Hour // 1 days, 1 hour
|
return 24 * time.Hour, time.Hour // 1 days, 1 hour
|
||||||
case certificatesDuration >= 24: // >= 1 days
|
case certificatesDuration >= 24: // >= 1 days
|
||||||
|
|
|
@ -613,12 +613,6 @@ func Test_getCertificateRenewDurations(t *testing.T) {
|
||||||
expectRenewPeriod: time.Hour * 24 * 30,
|
expectRenewPeriod: time.Hour * 24 * 30,
|
||||||
expectRenewInterval: time.Hour * 24,
|
expectRenewInterval: time.Hour * 24,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
desc: "30 Days certificates: 10 days renew period, 12 hour renew interval",
|
|
||||||
certificatesDurations: 24 * 30,
|
|
||||||
expectRenewPeriod: time.Hour * 24 * 10,
|
|
||||||
expectRenewInterval: time.Hour * 12,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
desc: "7 Days certificates: 1 days renew period, 1 hour renew interval",
|
desc: "7 Days certificates: 1 days renew period, 1 hour renew interval",
|
||||||
certificatesDurations: 24 * 7,
|
certificatesDurations: 24 * 7,
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
package docker
|
package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
dockertypes "github.com/docker/docker/api/types"
|
docker "github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/api/types/network"
|
"github.com/docker/docker/api/types/network"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
)
|
)
|
||||||
|
|
||||||
func containerJSON(ops ...func(*dockertypes.ContainerJSON)) dockertypes.ContainerJSON {
|
func containerJSON(ops ...func(*docker.ContainerJSON)) docker.ContainerJSON {
|
||||||
c := &dockertypes.ContainerJSON{
|
c := &docker.ContainerJSON{
|
||||||
ContainerJSONBase: &dockertypes.ContainerJSONBase{
|
ContainerJSONBase: &docker.ContainerJSONBase{
|
||||||
Name: "fake",
|
Name: "fake",
|
||||||
HostConfig: &container.HostConfig{},
|
HostConfig: &container.HostConfig{},
|
||||||
},
|
},
|
||||||
Config: &container.Config{},
|
Config: &container.Config{},
|
||||||
NetworkSettings: &dockertypes.NetworkSettings{
|
NetworkSettings: &docker.NetworkSettings{
|
||||||
NetworkSettingsBase: dockertypes.NetworkSettingsBase{},
|
NetworkSettingsBase: docker.NetworkSettingsBase{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,34 +27,34 @@ func containerJSON(ops ...func(*dockertypes.ContainerJSON)) dockertypes.Containe
|
||||||
return *c
|
return *c
|
||||||
}
|
}
|
||||||
|
|
||||||
func name(name string) func(*dockertypes.ContainerJSON) {
|
func name(name string) func(*docker.ContainerJSON) {
|
||||||
return func(c *dockertypes.ContainerJSON) {
|
return func(c *docker.ContainerJSON) {
|
||||||
c.ContainerJSONBase.Name = name
|
c.ContainerJSONBase.Name = name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func networkMode(mode string) func(*dockertypes.ContainerJSON) {
|
func networkMode(mode string) func(*docker.ContainerJSON) {
|
||||||
return func(c *dockertypes.ContainerJSON) {
|
return func(c *docker.ContainerJSON) {
|
||||||
c.ContainerJSONBase.HostConfig.NetworkMode = container.NetworkMode(mode)
|
c.ContainerJSONBase.HostConfig.NetworkMode = container.NetworkMode(mode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func nodeIP(ip string) func(*dockertypes.ContainerJSON) {
|
func nodeIP(ip string) func(*docker.ContainerJSON) {
|
||||||
return func(c *dockertypes.ContainerJSON) {
|
return func(c *docker.ContainerJSON) {
|
||||||
c.ContainerJSONBase.Node = &dockertypes.ContainerNode{
|
c.ContainerJSONBase.Node = &docker.ContainerNode{
|
||||||
IPAddress: ip,
|
IPAddress: ip,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ports(portMap nat.PortMap) func(*dockertypes.ContainerJSON) {
|
func ports(portMap nat.PortMap) func(*docker.ContainerJSON) {
|
||||||
return func(c *dockertypes.ContainerJSON) {
|
return func(c *docker.ContainerJSON) {
|
||||||
c.NetworkSettings.NetworkSettingsBase.Ports = portMap
|
c.NetworkSettings.NetworkSettingsBase.Ports = portMap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func withNetwork(name string, ops ...func(*network.EndpointSettings)) func(*dockertypes.ContainerJSON) {
|
func withNetwork(name string, ops ...func(*network.EndpointSettings)) func(*docker.ContainerJSON) {
|
||||||
return func(c *dockertypes.ContainerJSON) {
|
return func(c *docker.ContainerJSON) {
|
||||||
if c.NetworkSettings.Networks == nil {
|
if c.NetworkSettings.Networks == nil {
|
||||||
c.NetworkSettings.Networks = map[string]*network.EndpointSettings{}
|
c.NetworkSettings.Networks = map[string]*network.EndpointSettings{}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
docker "github.com/docker/docker/api/types"
|
docker "github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/network"
|
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -3973,12 +3972,12 @@ func TestDynConfBuilder_getIPAddress_swarm(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
service swarm.Service
|
service swarm.Service
|
||||||
expected string
|
expected string
|
||||||
networks map[string]*network.Summary
|
networks map[string]*docker.NetworkResource
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
service: swarmService(withEndpointSpec(modeDNSSR)),
|
service: swarmService(withEndpointSpec(modeDNSSR)),
|
||||||
expected: "",
|
expected: "",
|
||||||
networks: map[string]*network.Summary{},
|
networks: map[string]*docker.NetworkResource{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
service: swarmService(
|
service: swarmService(
|
||||||
|
@ -3986,7 +3985,7 @@ func TestDynConfBuilder_getIPAddress_swarm(t *testing.T) {
|
||||||
withEndpoint(virtualIP("1", "10.11.12.13/24")),
|
withEndpoint(virtualIP("1", "10.11.12.13/24")),
|
||||||
),
|
),
|
||||||
expected: "10.11.12.13",
|
expected: "10.11.12.13",
|
||||||
networks: map[string]*network.Summary{
|
networks: map[string]*docker.NetworkResource{
|
||||||
"1": {
|
"1": {
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
},
|
},
|
||||||
|
@ -4004,7 +4003,7 @@ func TestDynConfBuilder_getIPAddress_swarm(t *testing.T) {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
expected: "10.11.12.99",
|
expected: "10.11.12.99",
|
||||||
networks: map[string]*network.Summary{
|
networks: map[string]*docker.NetworkResource{
|
||||||
"1": {
|
"1": {
|
||||||
Name: "foonet",
|
Name: "foonet",
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/network"
|
dockertypes "github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -18,7 +18,7 @@ func TestListTasks(t *testing.T) {
|
||||||
tasks []swarm.Task
|
tasks []swarm.Task
|
||||||
isGlobalSVC bool
|
isGlobalSVC bool
|
||||||
expectedTasks []string
|
expectedTasks []string
|
||||||
networks map[string]*network.Summary
|
networks map[string]*dockertypes.NetworkResource
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
service: swarmService(serviceName("container")),
|
service: swarmService(serviceName("container")),
|
||||||
|
@ -53,7 +53,7 @@ func TestListTasks(t *testing.T) {
|
||||||
"container.1",
|
"container.1",
|
||||||
"container.4",
|
"container.4",
|
||||||
},
|
},
|
||||||
networks: map[string]*network.Summary{
|
networks: map[string]*dockertypes.NetworkResource{
|
||||||
"1": {
|
"1": {
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
},
|
},
|
||||||
|
@ -91,7 +91,7 @@ func TestSwarmProvider_listServices(t *testing.T) {
|
||||||
services []swarm.Service
|
services []swarm.Service
|
||||||
tasks []swarm.Task
|
tasks []swarm.Task
|
||||||
dockerVersion string
|
dockerVersion string
|
||||||
networks []network.Summary
|
networks []dockertypes.NetworkResource
|
||||||
expectedServices []string
|
expectedServices []string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
@ -117,7 +117,7 @@ func TestSwarmProvider_listServices(t *testing.T) {
|
||||||
withEndpointSpec(modeDNSSR)),
|
withEndpointSpec(modeDNSSR)),
|
||||||
},
|
},
|
||||||
dockerVersion: "1.30",
|
dockerVersion: "1.30",
|
||||||
networks: []network.Summary{},
|
networks: []dockertypes.NetworkResource{},
|
||||||
expectedServices: []string{},
|
expectedServices: []string{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -143,7 +143,7 @@ func TestSwarmProvider_listServices(t *testing.T) {
|
||||||
withEndpointSpec(modeDNSSR)),
|
withEndpointSpec(modeDNSSR)),
|
||||||
},
|
},
|
||||||
dockerVersion: "1.30",
|
dockerVersion: "1.30",
|
||||||
networks: []network.Summary{
|
networks: []dockertypes.NetworkResource{
|
||||||
{
|
{
|
||||||
Name: "network_name",
|
Name: "network_name",
|
||||||
ID: "yk6l57rfwizjzxxzftn4amaot",
|
ID: "yk6l57rfwizjzxxzftn4amaot",
|
||||||
|
@ -198,7 +198,7 @@ func TestSwarmProvider_listServices(t *testing.T) {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
dockerVersion: "1.30",
|
dockerVersion: "1.30",
|
||||||
networks: []network.Summary{
|
networks: []dockertypes.NetworkResource{
|
||||||
{
|
{
|
||||||
Name: "network_name",
|
Name: "network_name",
|
||||||
ID: "yk6l57rfwizjzxxzftn4amaot",
|
ID: "yk6l57rfwizjzxxzftn4amaot",
|
||||||
|
@ -255,7 +255,7 @@ func TestSwarmProvider_parseService_task(t *testing.T) {
|
||||||
tasks []swarm.Task
|
tasks []swarm.Task
|
||||||
isGlobalSVC bool
|
isGlobalSVC bool
|
||||||
expected map[string]dockerData
|
expected map[string]dockerData
|
||||||
networks map[string]*network.Summary
|
networks map[string]*dockertypes.NetworkResource
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
service: swarmService(serviceName("container")),
|
service: swarmService(serviceName("container")),
|
||||||
|
@ -276,7 +276,7 @@ func TestSwarmProvider_parseService_task(t *testing.T) {
|
||||||
Name: "container.3",
|
Name: "container.3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
networks: map[string]*network.Summary{
|
networks: map[string]*dockertypes.NetworkResource{
|
||||||
"1": {
|
"1": {
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
},
|
},
|
||||||
|
@ -301,7 +301,7 @@ func TestSwarmProvider_parseService_task(t *testing.T) {
|
||||||
Name: "container.id3",
|
Name: "container.id3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
networks: map[string]*network.Summary{
|
networks: map[string]*dockertypes.NetworkResource{
|
||||||
"1": {
|
"1": {
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
},
|
},
|
||||||
|
@ -339,7 +339,7 @@ func TestSwarmProvider_parseService_task(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
networks: map[string]*network.Summary{
|
networks: map[string]*dockertypes.NetworkResource{
|
||||||
"1": {
|
"1": {
|
||||||
Name: "vlan",
|
Name: "vlan",
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,7 +2,6 @@ package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
@ -102,8 +101,6 @@ func parseContainer(container dockertypes.ContainerJSON) dockerData {
|
||||||
type ClientConfig struct {
|
type ClientConfig struct {
|
||||||
apiVersion string
|
apiVersion string
|
||||||
|
|
||||||
Username string `description:"Username for Basic HTTP authentication." json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty"`
|
|
||||||
Password string `description:"Password for Basic HTTP authentication." json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty"`
|
|
||||||
Endpoint string `description:"Docker server endpoint. Can be a TCP or a Unix socket endpoint." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"`
|
Endpoint string `description:"Docker server endpoint. Can be a TCP or a Unix socket endpoint." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"`
|
||||||
TLS *types.ClientTLS `description:"Enable Docker TLS support." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
|
TLS *types.ClientTLS `description:"Enable Docker TLS support." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
|
||||||
HTTPClientTimeout ptypes.Duration `description:"Client timeout for HTTP connections." json:"httpClientTimeout,omitempty" toml:"httpClientTimeout,omitempty" yaml:"httpClientTimeout,omitempty" export:"true"`
|
HTTPClientTimeout ptypes.Duration `description:"Client timeout for HTTP connections." json:"httpClientTimeout,omitempty" toml:"httpClientTimeout,omitempty" yaml:"httpClientTimeout,omitempty" export:"true"`
|
||||||
|
@ -118,9 +115,6 @@ func createClient(ctx context.Context, cfg ClientConfig) (*client.Client, error)
|
||||||
httpHeaders := map[string]string{
|
httpHeaders := map[string]string{
|
||||||
"User-Agent": "Traefik " + version.Version,
|
"User-Agent": "Traefik " + version.Version,
|
||||||
}
|
}
|
||||||
if cfg.Username != "" && cfg.Password != "" {
|
|
||||||
httpHeaders["Authorization"] = "Basic " + base64.StdEncoding.EncodeToString([]byte(cfg.Username+":"+cfg.Password))
|
|
||||||
}
|
|
||||||
|
|
||||||
opts = append(opts,
|
opts = append(opts,
|
||||||
client.WithHTTPHeaders(httpHeaders),
|
client.WithHTTPHeaders(httpHeaders),
|
||||||
|
|
|
@ -57,9 +57,7 @@ type clientWrapper struct {
|
||||||
csCrd traefikclientset.Interface
|
csCrd traefikclientset.Interface
|
||||||
csKube kclientset.Interface
|
csKube kclientset.Interface
|
||||||
|
|
||||||
clusterScopeFactory kinformers.SharedInformerFactory
|
factoryClusterScope kinformers.SharedInformerFactory
|
||||||
disableClusterScopeInformer bool
|
|
||||||
|
|
||||||
factoriesCrd map[string]traefikinformers.SharedInformerFactory
|
factoriesCrd map[string]traefikinformers.SharedInformerFactory
|
||||||
factoriesKube map[string]kinformers.SharedInformerFactory
|
factoriesKube map[string]kinformers.SharedInformerFactory
|
||||||
factoriesSecret map[string]kinformers.SharedInformerFactory
|
factoriesSecret map[string]kinformers.SharedInformerFactory
|
||||||
|
@ -239,11 +237,18 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
|
||||||
c.factoriesSecret[ns] = factorySecret
|
c.factoriesSecret[ns] = factorySecret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.factoryClusterScope = kinformers.NewSharedInformerFactory(c.csKube, resyncPeriod)
|
||||||
|
_, err := c.factoryClusterScope.Core().V1().Nodes().Informer().AddEventHandler(eventHandler)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
for _, ns := range namespaces {
|
for _, ns := range namespaces {
|
||||||
c.factoriesCrd[ns].Start(stopCh)
|
c.factoriesCrd[ns].Start(stopCh)
|
||||||
c.factoriesKube[ns].Start(stopCh)
|
c.factoriesKube[ns].Start(stopCh)
|
||||||
c.factoriesSecret[ns].Start(stopCh)
|
c.factoriesSecret[ns].Start(stopCh)
|
||||||
}
|
}
|
||||||
|
c.factoryClusterScope.Start(stopCh)
|
||||||
|
|
||||||
for _, ns := range namespaces {
|
for _, ns := range namespaces {
|
||||||
for t, ok := range c.factoriesCrd[ns].WaitForCacheSync(stopCh) {
|
for t, ok := range c.factoriesCrd[ns].WaitForCacheSync(stopCh) {
|
||||||
|
@ -265,21 +270,11 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.disableClusterScopeInformer {
|
for t, ok := range c.factoryClusterScope.WaitForCacheSync(stopCh) {
|
||||||
c.clusterScopeFactory = kinformers.NewSharedInformerFactory(c.csKube, resyncPeriod)
|
|
||||||
_, err := c.clusterScopeFactory.Core().V1().Nodes().Informer().AddEventHandler(eventHandler)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.clusterScopeFactory.Start(stopCh)
|
|
||||||
|
|
||||||
for t, ok := range c.clusterScopeFactory.WaitForCacheSync(stopCh) {
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("timed out waiting for controller caches to sync %s", t.String())
|
return nil, fmt.Errorf("timed out waiting for controller caches to sync %s", t.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return eventCh, nil
|
return eventCh, nil
|
||||||
}
|
}
|
||||||
|
@ -479,7 +474,7 @@ func (c *clientWrapper) GetSecret(namespace, name string) (*corev1.Secret, bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *clientWrapper) GetNodes() ([]*corev1.Node, bool, error) {
|
func (c *clientWrapper) GetNodes() ([]*corev1.Node, bool, error) {
|
||||||
nodes, err := c.clusterScopeFactory.Core().V1().Nodes().Lister().List(labels.Everything())
|
nodes, err := c.factoryClusterScope.Core().V1().Nodes().Lister().List(labels.Everything())
|
||||||
exist, err := translateNotFoundError(err)
|
exist, err := translateNotFoundError(err)
|
||||||
return nodes, exist, err
|
return nodes, exist, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,6 @@ type Provider struct {
|
||||||
ThrottleDuration ptypes.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"`
|
ThrottleDuration ptypes.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"`
|
||||||
AllowEmptyServices bool `description:"Allow the creation of services without endpoints." json:"allowEmptyServices,omitempty" toml:"allowEmptyServices,omitempty" yaml:"allowEmptyServices,omitempty" export:"true"`
|
AllowEmptyServices bool `description:"Allow the creation of services without endpoints." json:"allowEmptyServices,omitempty" toml:"allowEmptyServices,omitempty" yaml:"allowEmptyServices,omitempty" export:"true"`
|
||||||
NativeLBByDefault bool `description:"Defines whether to use Native Kubernetes load-balancing mode by default." json:"nativeLBByDefault,omitempty" toml:"nativeLBByDefault,omitempty" yaml:"nativeLBByDefault,omitempty" export:"true"`
|
NativeLBByDefault bool `description:"Defines whether to use Native Kubernetes load-balancing mode by default." json:"nativeLBByDefault,omitempty" toml:"nativeLBByDefault,omitempty" yaml:"nativeLBByDefault,omitempty" export:"true"`
|
||||||
DisableClusterScopeResources bool `description:"Disables the lookup of cluster scope resources (incompatible with IngressClasses and NodePortLB enabled services)." json:"disableClusterScopeResources,omitempty" toml:"disableClusterScopeResources,omitempty" yaml:"disableClusterScopeResources,omitempty" export:"true"`
|
|
||||||
|
|
||||||
lastConfiguration safe.Safe
|
lastConfiguration safe.Safe
|
||||||
|
|
||||||
|
@ -113,7 +112,6 @@ func (p *Provider) newK8sClient(ctx context.Context) (*clientWrapper, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
client.labelSelector = p.LabelSelector
|
client.labelSelector = p.LabelSelector
|
||||||
client.disableClusterScopeInformer = p.DisableClusterScopeResources
|
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,7 +302,7 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
|
||||||
InFlightReq: middleware.Spec.InFlightReq,
|
InFlightReq: middleware.Spec.InFlightReq,
|
||||||
Buffering: middleware.Spec.Buffering,
|
Buffering: middleware.Spec.Buffering,
|
||||||
CircuitBreaker: circuitBreaker,
|
CircuitBreaker: circuitBreaker,
|
||||||
Compress: createCompressMiddleware(middleware.Spec.Compress),
|
Compress: middleware.Spec.Compress,
|
||||||
PassTLSClientCert: middleware.Spec.PassTLSClientCert,
|
PassTLSClientCert: middleware.Spec.PassTLSClientCert,
|
||||||
Retry: retry,
|
Retry: retry,
|
||||||
ContentType: middleware.Spec.ContentType,
|
ContentType: middleware.Spec.ContentType,
|
||||||
|
@ -658,49 +656,14 @@ func createCircuitBreakerMiddleware(circuitBreaker *traefikv1alpha1.CircuitBreak
|
||||||
return cb, nil
|
return cb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createCompressMiddleware(compress *traefikv1alpha1.Compress) *dynamic.Compress {
|
|
||||||
if compress == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
c := &dynamic.Compress{}
|
|
||||||
c.SetDefaults()
|
|
||||||
|
|
||||||
if compress.ExcludedContentTypes != nil {
|
|
||||||
c.ExcludedContentTypes = compress.ExcludedContentTypes
|
|
||||||
}
|
|
||||||
|
|
||||||
if compress.IncludedContentTypes != nil {
|
|
||||||
c.IncludedContentTypes = compress.IncludedContentTypes
|
|
||||||
}
|
|
||||||
|
|
||||||
if compress.MinResponseBodyBytes != nil {
|
|
||||||
c.MinResponseBodyBytes = *compress.MinResponseBodyBytes
|
|
||||||
}
|
|
||||||
|
|
||||||
if compress.Encodings != nil {
|
|
||||||
c.Encodings = compress.Encodings
|
|
||||||
}
|
|
||||||
|
|
||||||
if compress.DefaultEncoding != nil {
|
|
||||||
c.DefaultEncoding = *compress.DefaultEncoding
|
|
||||||
}
|
|
||||||
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func createRateLimitMiddleware(rateLimit *traefikv1alpha1.RateLimit) (*dynamic.RateLimit, error) {
|
func createRateLimitMiddleware(rateLimit *traefikv1alpha1.RateLimit) (*dynamic.RateLimit, error) {
|
||||||
if rateLimit == nil {
|
if rateLimit == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
rl := &dynamic.RateLimit{}
|
rl := &dynamic.RateLimit{Average: rateLimit.Average}
|
||||||
rl.SetDefaults()
|
rl.SetDefaults()
|
||||||
|
|
||||||
if rateLimit.Average != nil {
|
|
||||||
rl.Average = *rateLimit.Average
|
|
||||||
}
|
|
||||||
|
|
||||||
if rateLimit.Burst != nil {
|
if rateLimit.Burst != nil {
|
||||||
rl.Burst = *rateLimit.Burst
|
rl.Burst = *rateLimit.Burst
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,8 +55,7 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
|
||||||
allowCrossNamespace: p.AllowCrossNamespace,
|
allowCrossNamespace: p.AllowCrossNamespace,
|
||||||
allowExternalNameServices: p.AllowExternalNameServices,
|
allowExternalNameServices: p.AllowExternalNameServices,
|
||||||
allowEmptyServices: p.AllowEmptyServices,
|
allowEmptyServices: p.AllowEmptyServices,
|
||||||
nativeLBByDefault: p.NativeLBByDefault,
|
NativeLBByDefault: p.NativeLBByDefault,
|
||||||
disableClusterScopeResources: p.DisableClusterScopeResources,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, route := range ingressRoute.Spec.Routes {
|
for _, route := range ingressRoute.Spec.Routes {
|
||||||
|
@ -204,8 +203,7 @@ type configBuilder struct {
|
||||||
allowCrossNamespace bool
|
allowCrossNamespace bool
|
||||||
allowExternalNameServices bool
|
allowExternalNameServices bool
|
||||||
allowEmptyServices bool
|
allowEmptyServices bool
|
||||||
nativeLBByDefault bool
|
NativeLBByDefault bool
|
||||||
disableClusterScopeResources bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildTraefikService creates the configuration for the traefik service defined in tService,
|
// buildTraefikService creates the configuration for the traefik service defined in tService,
|
||||||
|
@ -430,7 +428,7 @@ func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.L
|
||||||
return []dynamic.Server{{URL: fmt.Sprintf("%s://%s", protocol, hostPort)}}, nil
|
return []dynamic.Server{{URL: fmt.Sprintf("%s://%s", protocol, hostPort)}}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
nativeLB := c.nativeLBByDefault
|
nativeLB := c.NativeLBByDefault
|
||||||
if svc.NativeLB != nil {
|
if svc.NativeLB != nil {
|
||||||
nativeLB = *svc.NativeLB
|
nativeLB = *svc.NativeLB
|
||||||
}
|
}
|
||||||
|
@ -450,10 +448,6 @@ func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.L
|
||||||
|
|
||||||
var servers []dynamic.Server
|
var servers []dynamic.Server
|
||||||
if service.Spec.Type == corev1.ServiceTypeNodePort && svc.NodePortLB {
|
if service.Spec.Type == corev1.ServiceTypeNodePort && svc.NodePortLB {
|
||||||
if c.disableClusterScopeResources {
|
|
||||||
return nil, errors.New("nodes lookup is disabled")
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes, nodesExists, nodesErr := c.client.GetNodes()
|
nodes, nodesExists, nodesErr := c.client.GetNodes()
|
||||||
if nodesErr != nil {
|
if nodesErr != nil {
|
||||||
return nil, nodesErr
|
return nil, nodesErr
|
||||||
|
|
|
@ -239,10 +239,6 @@ func (p *Provider) loadTCPServers(client Client, namespace string, svc traefikv1
|
||||||
|
|
||||||
var servers []dynamic.TCPServer
|
var servers []dynamic.TCPServer
|
||||||
if service.Spec.Type == corev1.ServiceTypeNodePort && svc.NodePortLB {
|
if service.Spec.Type == corev1.ServiceTypeNodePort && svc.NodePortLB {
|
||||||
if p.DisableClusterScopeResources {
|
|
||||||
return nil, errors.New("nodes lookup is disabled")
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes, nodesExists, nodesErr := client.GetNodes()
|
nodes, nodesExists, nodesErr := client.GetNodes()
|
||||||
if nodesErr != nil {
|
if nodesErr != nil {
|
||||||
return nil, nodesErr
|
return nil, nodesErr
|
||||||
|
|
|
@ -7447,8 +7447,8 @@ func TestNativeLB(t *testing.T) {
|
||||||
func TestNodePortLB(t *testing.T) {
|
func TestNodePortLB(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
ingressClass string
|
||||||
paths []string
|
paths []string
|
||||||
disableClusterScope bool
|
|
||||||
expected *dynamic.Configuration
|
expected *dynamic.Configuration
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
@ -7594,90 +7594,6 @@ func TestNodePortLB(t *testing.T) {
|
||||||
TLS: &dynamic.TLSConfiguration{},
|
TLS: &dynamic.TLSConfiguration{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
desc: "HTTP with node port LB, cluster scope resources disabled",
|
|
||||||
paths: []string{"services.yml", "with_node_port_lb.yml"},
|
|
||||||
disableClusterScope: true,
|
|
||||||
expected: &dynamic.Configuration{
|
|
||||||
UDP: &dynamic.UDPConfiguration{
|
|
||||||
Routers: map[string]*dynamic.UDPRouter{},
|
|
||||||
Services: map[string]*dynamic.UDPService{},
|
|
||||||
},
|
|
||||||
TCP: &dynamic.TCPConfiguration{
|
|
||||||
ServersTransports: map[string]*dynamic.TCPServersTransport{},
|
|
||||||
Routers: map[string]*dynamic.TCPRouter{},
|
|
||||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
|
||||||
Services: map[string]*dynamic.TCPService{},
|
|
||||||
},
|
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
|
||||||
ServersTransports: map[string]*dynamic.ServersTransport{},
|
|
||||||
Routers: map[string]*dynamic.Router{},
|
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
|
||||||
Services: map[string]*dynamic.Service{},
|
|
||||||
},
|
|
||||||
TLS: &dynamic.TLSConfiguration{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
desc: "TCP with native Service LB, cluster scope resources disabled",
|
|
||||||
paths: []string{"tcp/services.yml", "tcp/with_node_port_service_lb.yml"},
|
|
||||||
disableClusterScope: true,
|
|
||||||
expected: &dynamic.Configuration{
|
|
||||||
UDP: &dynamic.UDPConfiguration{
|
|
||||||
Routers: map[string]*dynamic.UDPRouter{},
|
|
||||||
Services: map[string]*dynamic.UDPService{},
|
|
||||||
},
|
|
||||||
TCP: &dynamic.TCPConfiguration{
|
|
||||||
ServersTransports: map[string]*dynamic.TCPServersTransport{},
|
|
||||||
Routers: map[string]*dynamic.TCPRouter{
|
|
||||||
"default-test.route-fdd3e9338e47a45efefc": {
|
|
||||||
EntryPoints: []string{"foo"},
|
|
||||||
Service: "default-test.route-fdd3e9338e47a45efefc",
|
|
||||||
Rule: "HostSNI(`foo.com`)",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
|
||||||
Services: map[string]*dynamic.TCPService{},
|
|
||||||
},
|
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
|
||||||
ServersTransports: map[string]*dynamic.ServersTransport{},
|
|
||||||
Routers: map[string]*dynamic.Router{},
|
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
|
||||||
Services: map[string]*dynamic.Service{},
|
|
||||||
},
|
|
||||||
TLS: &dynamic.TLSConfiguration{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "UDP with native Service LB, cluster scope resources disabled",
|
|
||||||
paths: []string{"udp/services.yml", "udp/with_node_port_service_lb.yml"},
|
|
||||||
disableClusterScope: true,
|
|
||||||
expected: &dynamic.Configuration{
|
|
||||||
UDP: &dynamic.UDPConfiguration{
|
|
||||||
Routers: map[string]*dynamic.UDPRouter{
|
|
||||||
"default-test.route-0": {
|
|
||||||
EntryPoints: []string{"foo"},
|
|
||||||
Service: "default-test.route-0",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Services: map[string]*dynamic.UDPService{},
|
|
||||||
},
|
|
||||||
TCP: &dynamic.TCPConfiguration{
|
|
||||||
ServersTransports: map[string]*dynamic.TCPServersTransport{},
|
|
||||||
Routers: map[string]*dynamic.TCPRouter{},
|
|
||||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
|
||||||
Services: map[string]*dynamic.TCPService{},
|
|
||||||
},
|
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
|
||||||
ServersTransports: map[string]*dynamic.ServersTransport{},
|
|
||||||
Routers: map[string]*dynamic.Router{},
|
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
|
||||||
Services: map[string]*dynamic.Service{},
|
|
||||||
},
|
|
||||||
TLS: &dynamic.TLSConfiguration{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
|
@ -7701,9 +7617,7 @@ func TestNodePortLB(t *testing.T) {
|
||||||
<-eventCh
|
<-eventCh
|
||||||
}
|
}
|
||||||
|
|
||||||
p := Provider{
|
p := Provider{}
|
||||||
DisableClusterScopeResources: test.disableClusterScope,
|
|
||||||
}
|
|
||||||
|
|
||||||
conf := p.loadConfigurationFromCRD(context.Background(), client)
|
conf := p.loadConfigurationFromCRD(context.Background(), client)
|
||||||
assert.Equal(t, test.expected, conf)
|
assert.Equal(t, test.expected, conf)
|
||||||
|
|
|
@ -123,10 +123,6 @@ func (p *Provider) loadUDPServers(client Client, namespace string, svc traefikv1
|
||||||
|
|
||||||
var servers []dynamic.UDPServer
|
var servers []dynamic.UDPServer
|
||||||
if service.Spec.Type == corev1.ServiceTypeNodePort && svc.NodePortLB {
|
if service.Spec.Type == corev1.ServiceTypeNodePort && svc.NodePortLB {
|
||||||
if p.DisableClusterScopeResources {
|
|
||||||
return nil, errors.New("nodes lookup is disabled")
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes, nodesExists, nodesErr := client.GetNodes()
|
nodes, nodesExists, nodesErr := client.GetNodes()
|
||||||
if nodesErr != nil {
|
if nodesErr != nil {
|
||||||
return nil, nodesErr
|
return nil, nodesErr
|
||||||
|
|
|
@ -46,7 +46,7 @@ type MiddlewareSpec struct {
|
||||||
InFlightReq *dynamic.InFlightReq `json:"inFlightReq,omitempty"`
|
InFlightReq *dynamic.InFlightReq `json:"inFlightReq,omitempty"`
|
||||||
Buffering *dynamic.Buffering `json:"buffering,omitempty"`
|
Buffering *dynamic.Buffering `json:"buffering,omitempty"`
|
||||||
CircuitBreaker *CircuitBreaker `json:"circuitBreaker,omitempty"`
|
CircuitBreaker *CircuitBreaker `json:"circuitBreaker,omitempty"`
|
||||||
Compress *Compress `json:"compress,omitempty"`
|
Compress *dynamic.Compress `json:"compress,omitempty"`
|
||||||
PassTLSClientCert *dynamic.PassTLSClientCert `json:"passTLSClientCert,omitempty"`
|
PassTLSClientCert *dynamic.PassTLSClientCert `json:"passTLSClientCert,omitempty"`
|
||||||
Retry *Retry `json:"retry,omitempty"`
|
Retry *Retry `json:"retry,omitempty"`
|
||||||
ContentType *dynamic.ContentType `json:"contentType,omitempty"`
|
ContentType *dynamic.ContentType `json:"contentType,omitempty"`
|
||||||
|
@ -188,7 +188,7 @@ type RateLimit struct {
|
||||||
// It defaults to 0, which means no rate limiting.
|
// It defaults to 0, which means no rate limiting.
|
||||||
// The rate is actually defined by dividing Average by Period. So for a rate below 1req/s,
|
// The rate is actually defined by dividing Average by Period. So for a rate below 1req/s,
|
||||||
// one needs to define a Period larger than a second.
|
// one needs to define a Period larger than a second.
|
||||||
Average *int64 `json:"average,omitempty"`
|
Average int64 `json:"average,omitempty"`
|
||||||
// Period, in combination with Average, defines the actual maximum rate, such as:
|
// Period, in combination with Average, defines the actual maximum rate, such as:
|
||||||
// r = Average / Period. It defaults to a second.
|
// r = Average / Period. It defaults to a second.
|
||||||
Period *intstr.IntOrString `json:"period,omitempty"`
|
Period *intstr.IntOrString `json:"period,omitempty"`
|
||||||
|
@ -203,26 +203,6 @@ type RateLimit struct {
|
||||||
|
|
||||||
// +k8s:deepcopy-gen=true
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// Compress holds the compress middleware configuration.
|
|
||||||
// This middleware compresses responses before sending them to the client, using gzip, brotli, or zstd compression.
|
|
||||||
// More info: https://doc.traefik.io/traefik/v3.1/middlewares/http/compress/
|
|
||||||
type Compress struct {
|
|
||||||
// ExcludedContentTypes defines the list of content types to compare the Content-Type header of the incoming requests and responses before compressing.
|
|
||||||
// `application/grpc` is always excluded.
|
|
||||||
ExcludedContentTypes []string `json:"excludedContentTypes,omitempty"`
|
|
||||||
// IncludedContentTypes defines the list of content types to compare the Content-Type header of the responses before compressing.
|
|
||||||
IncludedContentTypes []string `json:"includedContentTypes,omitempty"`
|
|
||||||
// MinResponseBodyBytes defines the minimum amount of bytes a response body must have to be compressed.
|
|
||||||
// Default: 1024.
|
|
||||||
MinResponseBodyBytes *int `json:"minResponseBodyBytes,omitempty"`
|
|
||||||
// Encodings defines the list of supported compression algorithms.
|
|
||||||
Encodings []string `json:"encodings,omitempty"`
|
|
||||||
// DefaultEncoding specifies the default encoding if the `Accept-Encoding` header is not in the request or contains a wildcard (`*`).
|
|
||||||
DefaultEncoding *string `json:"defaultEncoding,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// +k8s:deepcopy-gen=true
|
|
||||||
|
|
||||||
// Retry holds the retry middleware configuration.
|
// Retry holds the retry middleware configuration.
|
||||||
// This middleware reissues requests a given number of times to a backend server if that server does not reply.
|
// This middleware reissues requests a given number of times to a backend server if that server does not reply.
|
||||||
// As soon as the server answers, the middleware stops retrying, regardless of the response status.
|
// As soon as the server answers, the middleware stops retrying, regardless of the response status.
|
||||||
|
|
|
@ -164,47 +164,6 @@ func (in *ClientTLS) DeepCopy() *ClientTLS {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *Compress) DeepCopyInto(out *Compress) {
|
|
||||||
*out = *in
|
|
||||||
if in.ExcludedContentTypes != nil {
|
|
||||||
in, out := &in.ExcludedContentTypes, &out.ExcludedContentTypes
|
|
||||||
*out = make([]string, len(*in))
|
|
||||||
copy(*out, *in)
|
|
||||||
}
|
|
||||||
if in.IncludedContentTypes != nil {
|
|
||||||
in, out := &in.IncludedContentTypes, &out.IncludedContentTypes
|
|
||||||
*out = make([]string, len(*in))
|
|
||||||
copy(*out, *in)
|
|
||||||
}
|
|
||||||
if in.MinResponseBodyBytes != nil {
|
|
||||||
in, out := &in.MinResponseBodyBytes, &out.MinResponseBodyBytes
|
|
||||||
*out = new(int)
|
|
||||||
**out = **in
|
|
||||||
}
|
|
||||||
if in.Encodings != nil {
|
|
||||||
in, out := &in.Encodings, &out.Encodings
|
|
||||||
*out = make([]string, len(*in))
|
|
||||||
copy(*out, *in)
|
|
||||||
}
|
|
||||||
if in.DefaultEncoding != nil {
|
|
||||||
in, out := &in.DefaultEncoding, &out.DefaultEncoding
|
|
||||||
*out = new(string)
|
|
||||||
**out = **in
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Compress.
|
|
||||||
func (in *Compress) DeepCopy() *Compress {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(Compress)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *DigestAuth) DeepCopyInto(out *DigestAuth) {
|
func (in *DigestAuth) DeepCopyInto(out *DigestAuth) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
@ -817,7 +776,7 @@ func (in *MiddlewareSpec) DeepCopyInto(out *MiddlewareSpec) {
|
||||||
}
|
}
|
||||||
if in.Compress != nil {
|
if in.Compress != nil {
|
||||||
in, out := &in.Compress, &out.Compress
|
in, out := &in.Compress, &out.Compress
|
||||||
*out = new(Compress)
|
*out = new(dynamic.Compress)
|
||||||
(*in).DeepCopyInto(*out)
|
(*in).DeepCopyInto(*out)
|
||||||
}
|
}
|
||||||
if in.PassTLSClientCert != nil {
|
if in.PassTLSClientCert != nil {
|
||||||
|
@ -1016,11 +975,6 @@ func (in *ObjectReference) DeepCopy() *ObjectReference {
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *RateLimit) DeepCopyInto(out *RateLimit) {
|
func (in *RateLimit) DeepCopyInto(out *RateLimit) {
|
||||||
*out = *in
|
*out = *in
|
||||||
if in.Average != nil {
|
|
||||||
in, out := &in.Average, &out.Average
|
|
||||||
*out = new(int64)
|
|
||||||
**out = **in
|
|
||||||
}
|
|
||||||
if in.Period != nil {
|
if in.Period != nil {
|
||||||
in, out := &in.Period, &out.Period
|
in, out := &in.Period, &out.Period
|
||||||
*out = new(intstr.IntOrString)
|
*out = new(intstr.IntOrString)
|
||||||
|
|
|
@ -684,9 +684,32 @@ func translateNotFoundError(err error) (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func gatewayStatusEqual(statusA, statusB gatev1.GatewayStatus) bool {
|
func gatewayStatusEqual(statusA, statusB gatev1.GatewayStatus) bool {
|
||||||
return reflect.DeepEqual(statusA.Addresses, statusB.Addresses) &&
|
if len(statusA.Listeners) != len(statusB.Listeners) {
|
||||||
listenersStatusEqual(statusA.Listeners, statusB.Listeners) &&
|
return false
|
||||||
conditionsEqual(statusA.Conditions, statusB.Conditions)
|
}
|
||||||
|
|
||||||
|
if !conditionsEqual(statusA.Conditions, statusB.Conditions) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
listenerMatches := 0
|
||||||
|
for _, newListener := range statusB.Listeners {
|
||||||
|
for _, oldListener := range statusA.Listeners {
|
||||||
|
if newListener.Name == oldListener.Name {
|
||||||
|
if !conditionsEqual(newListener.Conditions, oldListener.Conditions) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if newListener.AttachedRoutes != oldListener.AttachedRoutes {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
listenerMatches++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return listenerMatches == len(statusA.Listeners)
|
||||||
}
|
}
|
||||||
|
|
||||||
func routeParentStatusesEqual(routeParentStatusesA, routeParentStatusesB []gatev1alpha2.RouteParentStatus) bool {
|
func routeParentStatusesEqual(routeParentStatusesA, routeParentStatusesB []gatev1alpha2.RouteParentStatus) bool {
|
||||||
|
@ -714,17 +737,15 @@ func routeParentStatusesEqual(routeParentStatusesA, routeParentStatusesB []gatev
|
||||||
}
|
}
|
||||||
|
|
||||||
func routeParentStatusEqual(sA, sB gatev1alpha2.RouteParentStatus) bool {
|
func routeParentStatusEqual(sA, sB gatev1alpha2.RouteParentStatus) bool {
|
||||||
return sA.ControllerName == sB.ControllerName &&
|
if !reflect.DeepEqual(sA.ParentRef, sB.ParentRef) {
|
||||||
reflect.DeepEqual(sA.ParentRef, sB.ParentRef) &&
|
return false
|
||||||
conditionsEqual(sA.Conditions, sB.Conditions)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func listenersStatusEqual(listenerA, listenerB []gatev1.ListenerStatus) bool {
|
if sA.ControllerName != sB.ControllerName {
|
||||||
return slices.EqualFunc(listenerA, listenerB, func(lA gatev1.ListenerStatus, lB gatev1.ListenerStatus) bool {
|
return false
|
||||||
return lA.Name == lB.Name &&
|
}
|
||||||
lA.AttachedRoutes == lB.AttachedRoutes &&
|
|
||||||
conditionsEqual(lA.Conditions, lB.Conditions)
|
return conditionsEqual(sA.Conditions, sB.Conditions)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func conditionsEqual(conditionsA, conditionsB []metav1.Condition) bool {
|
func conditionsEqual(conditionsA, conditionsB []metav1.Condition) bool {
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
---
|
|
||||||
kind: GatewayClass
|
|
||||||
apiVersion: gateway.networking.k8s.io/v1
|
|
||||||
metadata:
|
|
||||||
name: my-gateway-class
|
|
||||||
spec:
|
|
||||||
controllerName: traefik.io/gateway-controller
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: Gateway
|
|
||||||
apiVersion: gateway.networking.k8s.io/v1
|
|
||||||
metadata:
|
|
||||||
name: my-gateway
|
|
||||||
namespace: default
|
|
||||||
spec:
|
|
||||||
gatewayClassName: my-gateway-class
|
|
||||||
listeners: # Use GatewayClass defaults for listener definition.
|
|
||||||
- name: http
|
|
||||||
protocol: HTTP
|
|
||||||
port: 80
|
|
||||||
allowedRoutes:
|
|
||||||
kinds:
|
|
||||||
- kind: HTTPRoute
|
|
||||||
group: gateway.networking.k8s.io
|
|
||||||
namespaces:
|
|
||||||
from: Same
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: HTTPRoute
|
|
||||||
apiVersion: gateway.networking.k8s.io/v1
|
|
||||||
metadata:
|
|
||||||
name: http-app-1
|
|
||||||
namespace: default
|
|
||||||
spec:
|
|
||||||
parentRefs:
|
|
||||||
- name: my-gateway
|
|
||||||
kind: Gateway
|
|
||||||
group: gateway.networking.k8s.io
|
|
||||||
hostnames:
|
|
||||||
- "example.org"
|
|
||||||
rules:
|
|
||||||
- matches:
|
|
||||||
- path:
|
|
||||||
type: PathPrefix
|
|
||||||
value: /
|
|
||||||
backendRefs:
|
|
||||||
- name: whoami
|
|
||||||
port: 80
|
|
||||||
weight: 1
|
|
||||||
kind: Service
|
|
||||||
group: ""
|
|
||||||
filters:
|
|
||||||
- type: ResponseHeaderModifier
|
|
||||||
responseHeaderModifier:
|
|
||||||
set:
|
|
||||||
- name: X-Foo
|
|
||||||
value: Bar
|
|
||||||
add:
|
|
||||||
- name: X-Bar
|
|
||||||
value: Foo
|
|
||||||
remove:
|
|
||||||
- X-Baz
|
|
|
@ -316,9 +316,6 @@ func (p *Provider) loadMiddlewares(conf *dynamic.Configuration, namespace, route
|
||||||
case gatev1.HTTPRouteFilterRequestHeaderModifier:
|
case gatev1.HTTPRouteFilterRequestHeaderModifier:
|
||||||
middlewares[name] = createRequestHeaderModifier(filter.RequestHeaderModifier)
|
middlewares[name] = createRequestHeaderModifier(filter.RequestHeaderModifier)
|
||||||
|
|
||||||
case gatev1.HTTPRouteFilterResponseHeaderModifier:
|
|
||||||
middlewares[name] = createResponseHeaderModifier(filter.ResponseHeaderModifier)
|
|
||||||
|
|
||||||
case gatev1.HTTPRouteFilterExtensionRef:
|
case gatev1.HTTPRouteFilterExtensionRef:
|
||||||
name, middleware, err := p.loadHTTPRouteFilterExtensionRef(namespace, filter.ExtensionRef)
|
name, middleware, err := p.loadHTTPRouteFilterExtensionRef(namespace, filter.ExtensionRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -602,29 +599,7 @@ func createRequestHeaderModifier(filter *gatev1.HTTPHeaderFilter) *dynamic.Middl
|
||||||
}
|
}
|
||||||
|
|
||||||
return &dynamic.Middleware{
|
return &dynamic.Middleware{
|
||||||
RequestHeaderModifier: &dynamic.HeaderModifier{
|
RequestHeaderModifier: &dynamic.RequestHeaderModifier{
|
||||||
Set: sets,
|
|
||||||
Add: adds,
|
|
||||||
Remove: filter.Remove,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// createResponseHeaderModifier does not enforce/check the configuration,
|
|
||||||
// as the spec indicates that either the webhook or CEL (since v1.0 GA Release) should enforce that.
|
|
||||||
func createResponseHeaderModifier(filter *gatev1.HTTPHeaderFilter) *dynamic.Middleware {
|
|
||||||
sets := map[string]string{}
|
|
||||||
for _, header := range filter.Set {
|
|
||||||
sets[string(header.Name)] = header.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
adds := map[string]string{}
|
|
||||||
for _, header := range filter.Add {
|
|
||||||
adds[string(header.Name)] = header.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
return &dynamic.Middleware{
|
|
||||||
ResponseHeaderModifier: &dynamic.HeaderModifier{
|
|
||||||
Set: sets,
|
Set: sets,
|
||||||
Add: adds,
|
Add: adds,
|
||||||
Remove: filter.Remove,
|
Remove: filter.Remove,
|
||||||
|
|
|
@ -1722,77 +1722,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
||||||
},
|
},
|
||||||
Middlewares: map[string]*dynamic.Middleware{
|
Middlewares: map[string]*dynamic.Middleware{
|
||||||
"default-http-app-1-my-gateway-web-0-364ce6ec04c3d49b19c4-requestheadermodifier-0": {
|
"default-http-app-1-my-gateway-web-0-364ce6ec04c3d49b19c4-requestheadermodifier-0": {
|
||||||
RequestHeaderModifier: &dynamic.HeaderModifier{
|
RequestHeaderModifier: &dynamic.RequestHeaderModifier{
|
||||||
Set: map[string]string{"X-Foo": "Bar"},
|
|
||||||
Add: map[string]string{"X-Bar": "Foo"},
|
|
||||||
Remove: []string{"X-Baz"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Services: map[string]*dynamic.Service{
|
|
||||||
"default-http-app-1-my-gateway-web-0-wrr": {
|
|
||||||
Weighted: &dynamic.WeightedRoundRobin{
|
|
||||||
Services: []dynamic.WRRService{
|
|
||||||
{
|
|
||||||
Name: "default-whoami-80",
|
|
||||||
Weight: ptr.To(1),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"default-whoami-80": {
|
|
||||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
|
||||||
Servers: []dynamic.Server{
|
|
||||||
{
|
|
||||||
URL: "http://10.10.0.1:80",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
URL: "http://10.10.0.2:80",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
PassHostHeader: ptr.To(true),
|
|
||||||
ResponseForwarding: &dynamic.ResponseForwarding{
|
|
||||||
FlushInterval: ptypes.Duration(100 * time.Millisecond),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ServersTransports: map[string]*dynamic.ServersTransport{},
|
|
||||||
},
|
|
||||||
TLS: &dynamic.TLSConfiguration{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "Simple HTTPRoute, response header modifier",
|
|
||||||
paths: []string{"services.yml", "httproute/filter_response_header_modifier.yml"},
|
|
||||||
entryPoints: map[string]Entrypoint{"web": {
|
|
||||||
Address: ":80",
|
|
||||||
}},
|
|
||||||
expected: &dynamic.Configuration{
|
|
||||||
UDP: &dynamic.UDPConfiguration{
|
|
||||||
Routers: map[string]*dynamic.UDPRouter{},
|
|
||||||
Services: map[string]*dynamic.UDPService{},
|
|
||||||
},
|
|
||||||
TCP: &dynamic.TCPConfiguration{
|
|
||||||
Routers: map[string]*dynamic.TCPRouter{},
|
|
||||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
|
||||||
Services: map[string]*dynamic.TCPService{},
|
|
||||||
ServersTransports: map[string]*dynamic.TCPServersTransport{},
|
|
||||||
},
|
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
|
||||||
Routers: map[string]*dynamic.Router{
|
|
||||||
"default-http-app-1-my-gateway-web-0-364ce6ec04c3d49b19c4": {
|
|
||||||
EntryPoints: []string{"web"},
|
|
||||||
Service: "default-http-app-1-my-gateway-web-0-wrr",
|
|
||||||
Rule: "Host(`example.org`) && PathPrefix(`/`)",
|
|
||||||
Priority: 13,
|
|
||||||
RuleSyntax: "v3",
|
|
||||||
Middlewares: []string{"default-http-app-1-my-gateway-web-0-364ce6ec04c3d49b19c4-responseheadermodifier-0"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Middlewares: map[string]*dynamic.Middleware{
|
|
||||||
"default-http-app-1-my-gateway-web-0-364ce6ec04c3d49b19c4-responseheadermodifier-0": {
|
|
||||||
ResponseHeaderModifier: &dynamic.HeaderModifier{
|
|
||||||
Set: map[string]string{"X-Foo": "Bar"},
|
Set: map[string]string{"X-Foo": "Bar"},
|
||||||
Add: map[string]string{"X-Bar": "Foo"},
|
Add: map[string]string{"X-Bar": "Foo"},
|
||||||
Remove: []string{"X-Baz"},
|
Remove: []string{"X-Baz"},
|
||||||
|
|
|
@ -49,14 +49,14 @@ type Client interface {
|
||||||
|
|
||||||
type clientWrapper struct {
|
type clientWrapper struct {
|
||||||
clientset kclientset.Interface
|
clientset kclientset.Interface
|
||||||
clusterScopeFactory kinformers.SharedInformerFactory
|
factoryClusterScope kinformers.SharedInformerFactory
|
||||||
factoriesKube map[string]kinformers.SharedInformerFactory
|
factoriesKube map[string]kinformers.SharedInformerFactory
|
||||||
factoriesSecret map[string]kinformers.SharedInformerFactory
|
factoriesSecret map[string]kinformers.SharedInformerFactory
|
||||||
factoriesIngress map[string]kinformers.SharedInformerFactory
|
factoriesIngress map[string]kinformers.SharedInformerFactory
|
||||||
|
clusterFactory kinformers.SharedInformerFactory
|
||||||
ingressLabelSelector string
|
ingressLabelSelector string
|
||||||
isNamespaceAll bool
|
isNamespaceAll bool
|
||||||
disableIngressClassInformer bool // Deprecated.
|
disableIngressClassInformer bool
|
||||||
disableClusterScopeInformer bool
|
|
||||||
watchedNamespaces []string
|
watchedNamespaces []string
|
||||||
serverVersion *version.Version
|
serverVersion *version.Version
|
||||||
}
|
}
|
||||||
|
@ -201,54 +201,60 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
|
||||||
c.factoriesSecret[ns] = factorySecret
|
c.factoriesSecret[ns] = factorySecret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.factoryClusterScope = kinformers.NewSharedInformerFactory(c.clientset, resyncPeriod)
|
||||||
|
_, err = c.factoryClusterScope.Core().V1().Nodes().Informer().AddEventHandler(eventHandler)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
for _, ns := range namespaces {
|
for _, ns := range namespaces {
|
||||||
c.factoriesIngress[ns].Start(stopCh)
|
c.factoriesIngress[ns].Start(stopCh)
|
||||||
c.factoriesKube[ns].Start(stopCh)
|
c.factoriesKube[ns].Start(stopCh)
|
||||||
c.factoriesSecret[ns].Start(stopCh)
|
c.factoriesSecret[ns].Start(stopCh)
|
||||||
}
|
}
|
||||||
|
c.factoryClusterScope.Start(stopCh)
|
||||||
|
|
||||||
for _, ns := range namespaces {
|
for _, ns := range namespaces {
|
||||||
for t, ok := range c.factoriesIngress[ns].WaitForCacheSync(stopCh) {
|
for typ, ok := range c.factoriesIngress[ns].WaitForCacheSync(stopCh) {
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", t.String(), ns)
|
return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", typ, ns)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for t, ok := range c.factoriesKube[ns].WaitForCacheSync(stopCh) {
|
for typ, ok := range c.factoriesKube[ns].WaitForCacheSync(stopCh) {
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", t.String(), ns)
|
return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", typ, ns)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for t, ok := range c.factoriesSecret[ns].WaitForCacheSync(stopCh) {
|
for typ, ok := range c.factoriesSecret[ns].WaitForCacheSync(stopCh) {
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", t.String(), ns)
|
return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", typ, ns)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.disableIngressClassInformer || !c.disableClusterScopeInformer {
|
for t, ok := range c.factoryClusterScope.WaitForCacheSync(stopCh) {
|
||||||
c.clusterScopeFactory = kinformers.NewSharedInformerFactory(c.clientset, resyncPeriod)
|
|
||||||
|
|
||||||
_, err = c.clusterScopeFactory.Networking().V1().IngressClasses().Informer().AddEventHandler(eventHandler)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !c.disableClusterScopeInformer {
|
|
||||||
_, err = c.clusterScopeFactory.Core().V1().Nodes().Informer().AddEventHandler(eventHandler)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c.clusterScopeFactory.Start(stopCh)
|
|
||||||
|
|
||||||
for t, ok := range c.clusterScopeFactory.WaitForCacheSync(stopCh) {
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("timed out waiting for controller caches to sync %s", t.String())
|
return nil, fmt.Errorf("timed out waiting for controller caches to sync %s", t.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !c.disableIngressClassInformer {
|
||||||
|
c.clusterFactory = kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod)
|
||||||
|
|
||||||
|
_, err = c.clusterFactory.Networking().V1().IngressClasses().Informer().AddEventHandler(eventHandler)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.clusterFactory.Start(stopCh)
|
||||||
|
|
||||||
|
for typ, ok := range c.clusterFactory.WaitForCacheSync(stopCh) {
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("timed out waiting for controller caches to sync %s", typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return eventCh, nil
|
return eventCh, nil
|
||||||
|
@ -364,18 +370,18 @@ func (c *clientWrapper) GetSecret(namespace, name string) (*corev1.Secret, bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *clientWrapper) GetNodes() ([]*corev1.Node, bool, error) {
|
func (c *clientWrapper) GetNodes() ([]*corev1.Node, bool, error) {
|
||||||
nodes, err := c.clusterScopeFactory.Core().V1().Nodes().Lister().List(labels.Everything())
|
nodes, err := c.factoryClusterScope.Core().V1().Nodes().Lister().List(labels.Everything())
|
||||||
exist, err := translateNotFoundError(err)
|
exist, err := translateNotFoundError(err)
|
||||||
return nodes, exist, err
|
return nodes, exist, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *clientWrapper) GetIngressClasses() ([]*netv1.IngressClass, error) {
|
func (c *clientWrapper) GetIngressClasses() ([]*netv1.IngressClass, error) {
|
||||||
if c.clusterScopeFactory == nil {
|
if c.clusterFactory == nil {
|
||||||
return nil, errors.New("cluster factory not loaded")
|
return nil, errors.New("cluster factory not loaded")
|
||||||
}
|
}
|
||||||
|
|
||||||
var ics []*netv1.IngressClass
|
var ics []*netv1.IngressClass
|
||||||
ingressClasses, err := c.clusterScopeFactory.Networking().V1().IngressClasses().Lister().List(labels.Everything())
|
ingressClasses, err := c.clusterFactory.Networking().V1().IngressClasses().Lister().List(labels.Everything())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
kind: Ingress
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
metadata:
|
|
||||||
name: ""
|
|
||||||
namespace: testing
|
|
||||||
|
|
||||||
spec:
|
|
||||||
rules:
|
|
||||||
- host: traefik.tchouk
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /bar
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: service1
|
|
||||||
port:
|
|
||||||
number: 8080
|
|
||||||
pathType: Prefix
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: Service
|
|
||||||
apiVersion: v1
|
|
||||||
metadata:
|
|
||||||
name: service1
|
|
||||||
namespace: testing
|
|
||||||
annotations:
|
|
||||||
traefik.ingress.kubernetes.io/service.nodeportlb: "true"
|
|
||||||
|
|
||||||
spec:
|
|
||||||
ports:
|
|
||||||
- port: 8080
|
|
||||||
nodePort: 32456
|
|
||||||
clusterIP: 10.0.0.1
|
|
||||||
type: NodePort
|
|
||||||
externalName: traefik.wtf
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: Node
|
|
||||||
apiVersion: v1
|
|
||||||
metadata:
|
|
||||||
name: traefik-node
|
|
||||||
status:
|
|
||||||
addresses:
|
|
||||||
- type: InternalIP
|
|
||||||
address: 172.16.4.4
|
|
|
@ -51,9 +51,7 @@ type Provider struct {
|
||||||
ThrottleDuration ptypes.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"`
|
ThrottleDuration ptypes.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"`
|
||||||
AllowEmptyServices bool `description:"Allow creation of services without endpoints." json:"allowEmptyServices,omitempty" toml:"allowEmptyServices,omitempty" yaml:"allowEmptyServices,omitempty" export:"true"`
|
AllowEmptyServices bool `description:"Allow creation of services without endpoints." json:"allowEmptyServices,omitempty" toml:"allowEmptyServices,omitempty" yaml:"allowEmptyServices,omitempty" export:"true"`
|
||||||
AllowExternalNameServices bool `description:"Allow ExternalName services." json:"allowExternalNameServices,omitempty" toml:"allowExternalNameServices,omitempty" yaml:"allowExternalNameServices,omitempty" export:"true"`
|
AllowExternalNameServices bool `description:"Allow ExternalName services." json:"allowExternalNameServices,omitempty" toml:"allowExternalNameServices,omitempty" yaml:"allowExternalNameServices,omitempty" export:"true"`
|
||||||
// Deprecated: please use DisableClusterScopeResources.
|
DisableIngressClassLookup bool `description:"Disables the lookup of IngressClasses." json:"disableIngressClassLookup,omitempty" toml:"disableIngressClassLookup,omitempty" yaml:"disableIngressClassLookup,omitempty" export:"true"`
|
||||||
DisableIngressClassLookup bool `description:"Disables the lookup of IngressClasses (Deprecated, please use DisableClusterScopeResources)." json:"disableIngressClassLookup,omitempty" toml:"disableIngressClassLookup,omitempty" yaml:"disableIngressClassLookup,omitempty" export:"true"`
|
|
||||||
DisableClusterScopeResources bool `description:"Disables the lookup of cluster scope resources (incompatible with IngressClasses and NodePortLB enabled services)." json:"disableClusterScopeResources,omitempty" toml:"disableClusterScopeResources,omitempty" yaml:"disableClusterScopeResources,omitempty" export:"true"`
|
|
||||||
NativeLBByDefault bool `description:"Defines whether to use Native Kubernetes load-balancing mode by default." json:"nativeLBByDefault,omitempty" toml:"nativeLBByDefault,omitempty" yaml:"nativeLBByDefault,omitempty" export:"true"`
|
NativeLBByDefault bool `description:"Defines whether to use Native Kubernetes load-balancing mode by default." json:"nativeLBByDefault,omitempty" toml:"nativeLBByDefault,omitempty" yaml:"nativeLBByDefault,omitempty" export:"true"`
|
||||||
|
|
||||||
lastConfiguration safe.Safe
|
lastConfiguration safe.Safe
|
||||||
|
@ -116,8 +114,7 @@ func (p *Provider) newK8sClient(ctx context.Context) (*clientWrapper, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
cl.ingressLabelSelector = p.LabelSelector
|
cl.ingressLabelSelector = p.LabelSelector
|
||||||
cl.disableIngressClassInformer = p.DisableIngressClassLookup || p.DisableClusterScopeResources
|
cl.disableIngressClassInformer = p.DisableIngressClassLookup
|
||||||
cl.disableClusterScopeInformer = p.DisableClusterScopeResources
|
|
||||||
return cl, nil
|
return cl, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,6 +212,7 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Services: map[string]*dynamic.Service{},
|
Services: map[string]*dynamic.Service{},
|
||||||
},
|
},
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
}
|
}
|
||||||
|
|
||||||
var ingressClasses []*netv1.IngressClass
|
var ingressClasses []*netv1.IngressClass
|
||||||
|
@ -593,10 +591,6 @@ func (p *Provider) loadService(client Client, namespace string, backend netv1.In
|
||||||
}
|
}
|
||||||
|
|
||||||
if svcConfig.Service.NodePortLB && service.Spec.Type == corev1.ServiceTypeNodePort {
|
if svcConfig.Service.NodePortLB && service.Spec.Type == corev1.ServiceTypeNodePort {
|
||||||
if p.DisableClusterScopeResources {
|
|
||||||
return nil, errors.New("nodes lookup is disabled")
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes, nodesExists, nodesErr := client.GetNodes()
|
nodes, nodesExists, nodesErr := client.GetNodes()
|
||||||
if nodesErr != nil {
|
if nodesErr != nil {
|
||||||
return nil, nodesErr
|
return nil, nodesErr
|
||||||
|
|
|
@ -35,6 +35,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Empty ingresses",
|
desc: "Empty ingresses",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Routers: map[string]*dynamic.Router{},
|
Routers: map[string]*dynamic.Router{},
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
@ -45,6 +46,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress one rule host only",
|
desc: "Ingress one rule host only",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Routers: map[string]*dynamic.Router{},
|
Routers: map[string]*dynamic.Router{},
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
@ -55,6 +57,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with a basic rule on one path",
|
desc: "Ingress with a basic rule on one path",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -87,6 +90,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with annotations",
|
desc: "Ingress with annotations",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -144,6 +148,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with two different rules with one path",
|
desc: "Ingress with two different rules with one path",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -180,6 +185,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with conflicting routers on host",
|
desc: "Ingress with conflicting routers on host",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -216,6 +222,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with conflicting routers on path",
|
desc: "Ingress with conflicting routers on path",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -252,6 +259,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress one rule with two paths",
|
desc: "Ingress one rule with two paths",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -288,6 +296,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress one rule with one path and one host",
|
desc: "Ingress one rule with one path and one host",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -320,6 +329,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with one host without path",
|
desc: "Ingress with one host without path",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -349,6 +359,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress one rule with one host and two paths",
|
desc: "Ingress one rule with one host and two paths",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -385,6 +396,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress Two rules with one host and one path",
|
desc: "Ingress Two rules with one host and one path",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -421,6 +433,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with two services",
|
desc: "Ingress with two services",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -474,6 +487,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
desc: "Ingress with one service without endpoints subset",
|
desc: "Ingress with one service without endpoints subset",
|
||||||
allowEmptyServices: true,
|
allowEmptyServices: true,
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -498,6 +512,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with one service without endpoint",
|
desc: "Ingress with one service without endpoint",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{},
|
Routers: map[string]*dynamic.Router{},
|
||||||
|
@ -508,6 +523,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Single Service Ingress (without any rules)",
|
desc: "Single Service Ingress (without any rules)",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -542,6 +558,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with port value in backend and no pod replica",
|
desc: "Ingress with port value in backend and no pod replica",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -574,6 +591,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with port name in backend and no pod replica",
|
desc: "Ingress with port name in backend and no pod replica",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -606,6 +624,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with port name in backend and 2 pod replica",
|
desc: "Ingress with port name in backend and 2 pod replica",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -638,6 +657,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with two paths using same service and different port name",
|
desc: "Ingress with two paths using same service and different port name",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -690,6 +710,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with a named port matching subset of service pods",
|
desc: "Ingress with a named port matching subset of service pods",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -722,6 +743,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "2 ingresses in different namespace with same service name",
|
desc: "2 ingresses in different namespace with same service name",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -774,6 +796,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with unknown service port name",
|
desc: "Ingress with unknown service port name",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{},
|
Routers: map[string]*dynamic.Router{},
|
||||||
|
@ -784,6 +807,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with unknown service port",
|
desc: "Ingress with unknown service port",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{},
|
Routers: map[string]*dynamic.Router{},
|
||||||
|
@ -794,6 +818,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with port invalid for one service",
|
desc: "Ingress with port invalid for one service",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -823,6 +848,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "TLS support",
|
desc: "TLS support",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -863,6 +889,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with a basic rule on one path with https (port == 443)",
|
desc: "Ingress with a basic rule on one path with https (port == 443)",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -895,6 +922,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with a basic rule on one path with https (portname == https)",
|
desc: "Ingress with a basic rule on one path with https (portname == https)",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -927,6 +955,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with a basic rule on one path with https (portname starts with https)",
|
desc: "Ingress with a basic rule on one path with https (portname starts with https)",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
|
||||||
|
@ -960,6 +989,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Double Single Service Ingress",
|
desc: "Double Single Service Ingress",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -994,6 +1024,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with default traefik ingressClass",
|
desc: "Ingress with default traefik ingressClass",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -1023,6 +1054,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress without provider traefik ingressClass and unknown annotation",
|
desc: "Ingress without provider traefik ingressClass and unknown annotation",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{},
|
Routers: map[string]*dynamic.Router{},
|
||||||
|
@ -1034,6 +1066,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
desc: "Ingress with non matching provider traefik ingressClass and annotation",
|
desc: "Ingress with non matching provider traefik ingressClass and annotation",
|
||||||
ingressClass: "tchouk",
|
ingressClass: "tchouk",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{},
|
Routers: map[string]*dynamic.Router{},
|
||||||
|
@ -1045,6 +1078,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
desc: "Ingress with ingressClass without annotation",
|
desc: "Ingress with ingressClass without annotation",
|
||||||
ingressClass: "tchouk",
|
ingressClass: "tchouk",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{},
|
Routers: map[string]*dynamic.Router{},
|
||||||
|
@ -1056,6 +1090,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
desc: "Ingress with ingressClass without annotation",
|
desc: "Ingress with ingressClass without annotation",
|
||||||
ingressClass: "toto",
|
ingressClass: "toto",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{},
|
Routers: map[string]*dynamic.Router{},
|
||||||
|
@ -1066,6 +1101,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with wildcard host",
|
desc: "Ingress with wildcard host",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -1097,6 +1133,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with multiple ingressClasses",
|
desc: "Ingress with multiple ingressClasses",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -1131,6 +1168,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
desc: "Ingress with ingressClasses filter",
|
desc: "Ingress with ingressClasses filter",
|
||||||
ingressClass: "traefik-lb2",
|
ingressClass: "traefik-lb2",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -1160,6 +1198,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with prefix pathType",
|
desc: "Ingress with prefix pathType",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -1189,6 +1228,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with empty pathType",
|
desc: "Ingress with empty pathType",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -1218,6 +1258,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with exact pathType",
|
desc: "Ingress with exact pathType",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -1247,6 +1288,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with implementationSpecific pathType",
|
desc: "Ingress with implementationSpecific pathType",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -1276,6 +1318,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with ingress annotation",
|
desc: "Ingress with ingress annotation",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -1308,6 +1351,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
desc: "Ingress with ingress annotation",
|
desc: "Ingress with ingress annotation",
|
||||||
disableIngressClassLookup: true,
|
disableIngressClassLookup: true,
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -1337,6 +1381,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with ingressClass",
|
desc: "Ingress with ingressClass",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -1369,6 +1414,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
desc: "Ingress with ingressClass",
|
desc: "Ingress with ingressClass",
|
||||||
disableIngressClassLookup: true,
|
disableIngressClassLookup: true,
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{},
|
Routers: map[string]*dynamic.Router{},
|
||||||
|
@ -1379,6 +1425,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with named port",
|
desc: "Ingress with named port",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -1408,6 +1455,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with missing ingressClass",
|
desc: "Ingress with missing ingressClass",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{},
|
Routers: map[string]*dynamic.Router{},
|
||||||
|
@ -1418,6 +1466,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with defaultbackend",
|
desc: "Ingress with defaultbackend",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -1475,6 +1524,7 @@ func TestLoadConfigurationFromIngressesWithExternalNameServices(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with service with externalName",
|
desc: "Ingress with service with externalName",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{},
|
Routers: map[string]*dynamic.Router{},
|
||||||
|
@ -1486,6 +1536,7 @@ func TestLoadConfigurationFromIngressesWithExternalNameServices(t *testing.T) {
|
||||||
desc: "Ingress with service with externalName enabled",
|
desc: "Ingress with service with externalName enabled",
|
||||||
allowExternalNameServices: true,
|
allowExternalNameServices: true,
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -1515,6 +1566,7 @@ func TestLoadConfigurationFromIngressesWithExternalNameServices(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with IPv6 endpoints",
|
desc: "Ingress with IPv6 endpoints",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -1545,6 +1597,7 @@ func TestLoadConfigurationFromIngressesWithExternalNameServices(t *testing.T) {
|
||||||
desc: "Ingress with IPv6 endpoints externalname enabled",
|
desc: "Ingress with IPv6 endpoints externalname enabled",
|
||||||
allowExternalNameServices: true,
|
allowExternalNameServices: true,
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -1597,6 +1650,7 @@ func TestLoadConfigurationFromIngressesWithNativeLB(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with native service lb",
|
desc: "Ingress with native service lb",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -1640,12 +1694,13 @@ func TestLoadConfigurationFromIngressesWithNativeLB(t *testing.T) {
|
||||||
func TestLoadConfigurationFromIngressesWithNodePortLB(t *testing.T) {
|
func TestLoadConfigurationFromIngressesWithNodePortLB(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
clusterScopeDisabled bool
|
ingressClass string
|
||||||
expected *dynamic.Configuration
|
expected *dynamic.Configuration
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "Ingress with node port lb",
|
desc: "Ingress with node port lb",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -1670,17 +1725,6 @@ func TestLoadConfigurationFromIngressesWithNodePortLB(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
desc: "Ingress with node port lb cluster scope disabled",
|
|
||||||
clusterScopeDisabled: true,
|
|
||||||
expected: &dynamic.Configuration{
|
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
|
||||||
Routers: map[string]*dynamic.Router{},
|
|
||||||
Services: map[string]*dynamic.Service{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
|
@ -1689,7 +1733,7 @@ func TestLoadConfigurationFromIngressesWithNodePortLB(t *testing.T) {
|
||||||
|
|
||||||
clientMock := newClientMock(generateTestFilename(test.desc))
|
clientMock := newClientMock(generateTestFilename(test.desc))
|
||||||
|
|
||||||
p := Provider{DisableClusterScopeResources: test.clusterScopeDisabled}
|
p := Provider{IngressClass: test.ingressClass}
|
||||||
conf := p.loadConfigurationFromIngresses(context.Background(), clientMock)
|
conf := p.loadConfigurationFromIngresses(context.Background(), clientMock)
|
||||||
|
|
||||||
assert.Equal(t, test.expected, conf)
|
assert.Equal(t, test.expected, conf)
|
||||||
|
@ -1883,6 +1927,7 @@ func TestLoadConfigurationFromIngressesWithNativeLBByDefault(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with native service lb",
|
desc: "Ingress with native service lb",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
@ -1910,6 +1955,7 @@ func TestLoadConfigurationFromIngressesWithNativeLBByDefault(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Ingress with native lb by default",
|
desc: "Ingress with native lb by default",
|
||||||
expected: &dynamic.Configuration{
|
expected: &dynamic.Configuration{
|
||||||
|
TCP: &dynamic.TCPConfiguration{},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
|
|
|
@ -207,7 +207,6 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
"traefik/http/middlewares/Middleware02/buffering/retryExpression": "foobar",
|
"traefik/http/middlewares/Middleware02/buffering/retryExpression": "foobar",
|
||||||
"traefik/http/middlewares/Middleware02/buffering/maxRequestBodyBytes": "42",
|
"traefik/http/middlewares/Middleware02/buffering/maxRequestBodyBytes": "42",
|
||||||
"traefik/http/middlewares/Middleware02/buffering/memRequestBodyBytes": "42",
|
"traefik/http/middlewares/Middleware02/buffering/memRequestBodyBytes": "42",
|
||||||
"traefik/http/middlewares/Middleware05/compress/encodings": "foobar, foobar",
|
|
||||||
"traefik/http/middlewares/Middleware05/compress/minResponseBodyBytes": "42",
|
"traefik/http/middlewares/Middleware05/compress/minResponseBodyBytes": "42",
|
||||||
"traefik/http/middlewares/Middleware18/retry/attempts": "42",
|
"traefik/http/middlewares/Middleware18/retry/attempts": "42",
|
||||||
"traefik/http/middlewares/Middleware19/stripPrefix/prefixes/0": "foobar",
|
"traefik/http/middlewares/Middleware19/stripPrefix/prefixes/0": "foobar",
|
||||||
|
@ -413,10 +412,6 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
"Middleware05": {
|
"Middleware05": {
|
||||||
Compress: &dynamic.Compress{
|
Compress: &dynamic.Compress{
|
||||||
MinResponseBodyBytes: 42,
|
MinResponseBodyBytes: 42,
|
||||||
Encodings: []string{
|
|
||||||
"foobar",
|
|
||||||
"foobar",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Middleware08": {
|
"Middleware08": {
|
||||||
|
|
|
@ -397,15 +397,6 @@ func (b *Builder) buildConstructor(ctx context.Context, middlewareName string) (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.ResponseHeaderModifier != nil {
|
|
||||||
if middleware != nil {
|
|
||||||
return nil, badConf
|
|
||||||
}
|
|
||||||
middleware = func(next http.Handler) (http.Handler, error) {
|
|
||||||
return headermodifier.NewResponseHeaderModifier(ctx, next, *config.ResponseHeaderModifier, middlewareName), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.RequestRedirect != nil {
|
if config.RequestRedirect != nil {
|
||||||
if middleware != nil {
|
if middleware != nil {
|
||||||
return nil, badConf
|
return nil, badConf
|
||||||
|
|
|
@ -4,11 +4,11 @@ RepositoryName = "traefik"
|
||||||
OutputType = "file"
|
OutputType = "file"
|
||||||
FileName = "traefik_changelog.md"
|
FileName = "traefik_changelog.md"
|
||||||
|
|
||||||
# example new bugfix v3.1.2
|
# example new bugfix v3.1.1
|
||||||
CurrentRef = "v3.1"
|
CurrentRef = "v3.1"
|
||||||
PreviousRef = "v3.1.1"
|
PreviousRef = "v3.1.0"
|
||||||
BaseBranch = "v3.1"
|
BaseBranch = "v3.1"
|
||||||
FutureCurrentRefName = "v3.1.2"
|
FutureCurrentRefName = "v3.1.1"
|
||||||
|
|
||||||
ThresholdPreviousRef = 10
|
ThresholdPreviousRef = 10
|
||||||
ThresholdCurrentRef = 10
|
ThresholdCurrentRef = 10
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"transfer": "node dev/scripts/transfer.js",
|
"transfer": "node dev/scripts/transfer.js",
|
||||||
"lint": "eslint src/**/*.{js,vue}",
|
"lint": "eslint --ext .js,.vue src",
|
||||||
"dev": "APP_ENV=development quasar dev",
|
"dev": "APP_ENV=development quasar dev",
|
||||||
"build-quasar": "quasar build",
|
"build-quasar": "quasar build",
|
||||||
"build-staging": "NODE_ENV=production APP_ENV=development yarn build-quasar",
|
"build-staging": "NODE_ENV=production APP_ENV=development yarn build-quasar",
|
||||||
|
@ -18,8 +18,8 @@
|
||||||
"test:unit:ci": "vitest run"
|
"test:unit:ci": "vitest run"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@quasar/extras": "^1.16.12",
|
"@quasar/extras": "^1.16.9",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.6.7",
|
||||||
"bowser": "^2.11.0",
|
"bowser": "^2.11.0",
|
||||||
"chart.js": "^4.4.1",
|
"chart.js": "^4.4.1",
|
||||||
"core-js": "^3.35.1",
|
"core-js": "^3.35.1",
|
||||||
|
@ -27,7 +27,7 @@
|
||||||
"iframe-resizer": "^4.3.9",
|
"iframe-resizer": "^4.3.9",
|
||||||
"lodash.isequal": "4.5.0",
|
"lodash.isequal": "4.5.0",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"quasar": "^2.16.6",
|
"quasar": "^2.14.3",
|
||||||
"query-string": "^8.1.0",
|
"query-string": "^8.1.0",
|
||||||
"vh-check": "^2.0.5",
|
"vh-check": "^2.0.5",
|
||||||
"vue": "^3.0.0",
|
"vue": "^3.0.0",
|
||||||
|
@ -39,8 +39,8 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.23.9",
|
"@babel/core": "^7.23.9",
|
||||||
"@babel/eslint-parser": "^7.23.10",
|
"@babel/eslint-parser": "^7.23.10",
|
||||||
"@quasar/app-vite": "^2.0.0-beta.15",
|
"@quasar/app-vite": "^1.4.3",
|
||||||
"@quasar/babel-preset-app": "^2.0.3",
|
"@quasar/babel-preset-app": "^2.0.2",
|
||||||
"@quasar/quasar-app-extension-testing-unit-vitest": "^1.0.0",
|
"@quasar/quasar-app-extension-testing-unit-vitest": "^1.0.0",
|
||||||
"@vue/test-utils": "^2.4.4",
|
"@vue/test-utils": "^2.4.4",
|
||||||
"autoprefixer": "^10.4.2",
|
"autoprefixer": "^10.4.2",
|
||||||
|
@ -51,7 +51,7 @@
|
||||||
"eslint-plugin-promise": "^6.0.0",
|
"eslint-plugin-promise": "^6.0.0",
|
||||||
"eslint-plugin-vue": "^9.0.0",
|
"eslint-plugin-vue": "^9.0.0",
|
||||||
"postcss": "^8.4.14",
|
"postcss": "^8.4.14",
|
||||||
"vitest": "^1.6.0"
|
"vitest": "^1.3.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20 || ^18 || ^16",
|
"node": "^20 || ^18 || ^16",
|
||||||
|
|
4589
webui/yarn.lock
4589
webui/yarn.lock
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue