mirror of
https://github.com/containous/traefik.git
synced 2026-01-11 00:33:05 +03:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc04dc1940 | ||
|
|
26f4a669b8 | ||
|
|
e8067f4e01 | ||
|
|
1881434ac6 | ||
|
|
862488569d | ||
|
|
2b710f05b3 | ||
|
|
c7487c4a69 | ||
|
|
e9f3089e90 | ||
|
|
7e703742cb | ||
|
|
4de6d6b902 | ||
|
|
1778ff3bac | ||
|
|
7e1654ae27 | ||
|
|
5d00096f82 | ||
|
|
be27044099 | ||
|
|
dbebe5fa3e | ||
|
|
413b7c8cca | ||
|
|
3b6949c18c | ||
|
|
66bf4632e2 | ||
|
|
1de72c715d | ||
|
|
cabcf19303 | ||
|
|
47d7094dfb |
63
.github/workflows/check_doc.yaml
vendored
Normal file
63
.github/workflows/check_doc.yaml
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
name: Check Documentation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
paths:
|
||||
- '.github/workflows/check_doc.yaml'
|
||||
- 'docs/**'
|
||||
|
||||
jobs:
|
||||
|
||||
docs:
|
||||
name: lint, build and verify
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install markdownlint
|
||||
run: |
|
||||
npm install --global markdownlint@0.29.0 markdownlint-cli@0.35.0
|
||||
|
||||
- name: Lint
|
||||
run: ./docs/scripts/lint.sh docs
|
||||
|
||||
- name: Setup python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.12'
|
||||
cache: 'pip'
|
||||
cache-dependency-path: "./docs/requirements.txt"
|
||||
|
||||
- name: Build documentation
|
||||
working-directory: ./docs
|
||||
run: |
|
||||
pip install -r requirements.txt
|
||||
mkdocs build --strict
|
||||
|
||||
- name: Setup ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: '3.4'
|
||||
|
||||
- name: Install html-proofer
|
||||
run: |
|
||||
gem install nokogiri --version 1.18.6 --no-document -- --use-system-libraries
|
||||
gem install html-proofer --version 5.0.10 --no-document -- --use-system-libraries
|
||||
env:
|
||||
NOKOGIRI_USE_SYSTEM_LIBRARIES: "true"
|
||||
|
||||
# Comes from https://github.com/gjtorikian/html-proofer?tab=readme-ov-file#caching-with-continuous-integration
|
||||
- name: Cache HTMLProofer
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: tmp/.htmlproofer
|
||||
key: ${{ runner.os }}-htmlproofer
|
||||
|
||||
- name: Verify
|
||||
run: ./docs/scripts/verify.sh docs/site
|
||||
25
.github/workflows/check_doc.yml
vendored
25
.github/workflows/check_doc.yml
vendored
@@ -1,25 +0,0 @@
|
||||
name: Check Documentation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
|
||||
docs:
|
||||
name: Check, verify and build documentation
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check documentation
|
||||
run: make docs-pull-images docs
|
||||
env:
|
||||
# These variables are not passed to workflows that are triggered by a pull request from a fork.
|
||||
DOCS_VERIFY_SKIP: ${{ vars.DOCS_VERIFY_SKIP }}
|
||||
DOCS_LINT_SKIP: ${{ vars.DOCS_LINT_SKIP }}
|
||||
2
.github/workflows/validate.yaml
vendored
2
.github/workflows/validate.yaml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
env:
|
||||
GO_VERSION: '1.24'
|
||||
GOLANGCI_LINT_VERSION: v2.0.2
|
||||
MISSPELL_VERSION: v0.6.0
|
||||
MISSPELL_VERSION: v0.7.0
|
||||
|
||||
jobs:
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ RUN apk --no-cache --no-progress add \
|
||||
|
||||
COPY ./scripts/verify.sh /verify.sh
|
||||
COPY ./scripts/lint.sh /lint.sh
|
||||
COPY ./scripts/lint-yaml.sh /lint-yaml.sh
|
||||
|
||||
WORKDIR /app
|
||||
VOLUME ["/tmp","/app"]
|
||||
|
||||
18
docs/content/assets/css/code-copy.css
Normal file
18
docs/content/assets/css/code-copy.css
Normal file
@@ -0,0 +1,18 @@
|
||||
/* Fix positioning of the built-in clipboard button for code blocks.
|
||||
* In this theme, the button can end up positioned relative to <body>,
|
||||
* so anchor it to the code block container instead.
|
||||
*/
|
||||
|
||||
.md-typeset pre.highlight {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.md-typeset pre.highlight > button.md-clipboard {
|
||||
position: absolute;
|
||||
top: .25rem;
|
||||
right: .25rem;
|
||||
z-index: 10;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
148
docs/content/features/index.md
Normal file
148
docs/content/features/index.md
Normal file
@@ -0,0 +1,148 @@
|
||||
---
|
||||
title: "Traefik Product Features Comparison"
|
||||
description: "Compare features across Traefik Proxy, Traefik Hub API Gateway (including AI Gateway capabilities), and Traefik Hub API Management to choose the right solution for your needs."
|
||||
---
|
||||
|
||||
# Traefik Product Features Comparison
|
||||
|
||||
The Traefik ecosystem offers multiple products designed to meet different requirements, from basic reverse proxy functionality to comprehensive API management and AI gateway capabilities. This comparison matrix helps you understand the features available in each product and choose the right solution for your use case.
|
||||
|
||||
## Product Overview
|
||||
|
||||
- **Traefik Proxy** is the open-source application proxy that serves as the foundation for all Traefik products. It provides essential reverse proxy, load balancing, and service discovery capabilities.
|
||||
|
||||
- **[Traefik Hub API Gateway](https://traefik.io/solutions/api-gateway/)** builds on Traefik Proxy with enterprise-grade security, distributed features, and advanced access control for cloud-native API gateway scenarios. It includes **AI Gateway capabilities** that transform any AI endpoint into a managed API.
|
||||
|
||||
- **[Traefik Hub API Management](https://traefik.io/solutions/api-management/)** adds comprehensive API lifecycle management, developer portals, and organizational features for teams managing multiple APIs across environments.
|
||||
|
||||
- **[Traefik AI Gateway](https://traefik.io/solutions/ai-gateway/)** transforms any AI endpoint into a managed API with unified access to multiple LLMs, centralized credential management, semantic caching, local inferencing, and comprehensive AI governance features.
|
||||
|
||||
- **[Traefik MCP Gateway](https://traefik.io/solutions/mcp-gateway/)** provides secure, governed access to Model Context Protocol (MCP) servers for AI agents with task-based access control (TBAC), session-smart routing, and comprehensive audit capabilities for enterprise AI workflows.
|
||||
|
||||
## Features Matrix
|
||||
|
||||
| Feature | Traefik Proxy | Traefik Hub API Gateway | Traefik Hub API Management |
|
||||
|---------|---------------|------------------------|---------------------------|
|
||||
| **Core Networking** | | | |
|
||||
| Services Auto-Discovery | ✓ | ✓ | ✓ |
|
||||
| Graceful Configuration Reload | ✓ | ✓ | ✓ |
|
||||
| Websockets, HTTP/2, HTTP/3, TCP, UDP, GRPC | ✓ | ✓ | ✓ |
|
||||
| Real-time Logs, Access Logs, Metrics & Distributed Tracing | ✓ | ✓ | ✓ |
|
||||
| Canary Deployments | ✓ | ✓ | ✓ |
|
||||
| Let's Encrypt | ✓ | ✓ | ✓ |
|
||||
| **Plugin Ecosystem** | | | |
|
||||
| [Plugin Support](https://plugins.traefik.io/plugins) ([Go](https://github.com/traefik/yaegi), [WASM](https://webassembly.org/)) | ✓ | ✓ | ✓ |
|
||||
| **Deployment & Operations** | | | |
|
||||
| Hybrid cloud, multi-cloud & on-prem compatible | ✓ | ✓ | ✓ |
|
||||
| Per-cluster dashboard | ✓ | ✓ | ✓ |
|
||||
| GitOps-native declarative configuration | ✓ | ✓ | ✓ |
|
||||
| **Authentication & Authorization** | | | |
|
||||
| JWT Authentication | ✗ | ✓ | ✓ |
|
||||
| OAuth 2.0 Token Introspection Authentication | ✗ | ✓ | ✓ |
|
||||
| OAuth 2.0 Client Credentials Authentication | ✗ | ✓ | ✓ |
|
||||
| OpenID Connect Authentication | ✗ | ✓ | ✓ |
|
||||
| Lightweight Directory Access Protocol (LDAP) | ✗ | ✓ | ✓ |
|
||||
| API Key Authentication | ✗ | ✓ | ✓ |
|
||||
| **Security & Policy** | | | |
|
||||
| Open Policy Agent | ✗ | ✓ | ✓ |
|
||||
| Native Coraza Web Application Firewall (WAF) | ✗ | ✓ | ✓ |
|
||||
| HashiCorp Vault Integration | ✗ | ✓ | ✓ |
|
||||
| **Distributed Features** | | | |
|
||||
| Distributed Let's Encrypt | ✗ | ✓ | ✓ |
|
||||
| Distributed Rate Limit | ✗ | ✓ | ✓ |
|
||||
| HTTP Caching | ✗ | ✓ | ✓ |
|
||||
| **Compliance** | | | |
|
||||
| FIPS 140-2 Compliance (Linux & Windows) | ✗ | ✓ | ✓ |
|
||||
| **AI Gateway Capabilities** | | | |
|
||||
| Unified Multi-LLM API Access | ✗ | ✓ | ✓ |
|
||||
| Centralized AI Credential Management | ✗ | ✓ | ✓ |
|
||||
| AI Provider Flexibility (OpenAI, Anthropic, Azure OpenAI, AWS Bedrock, etc.) | ✗ | ✓ | ✓ |
|
||||
| Semantic Caching for AI Responses | ✗ | ✓ | ✓ |
|
||||
| Content Guard & PII Protection | ✗ | ✓ | ✓ |
|
||||
| AI-specific Observability & OpenTelemetry Integration | ✗ | ✓ | ✓ |
|
||||
| Support for Local/Self-hosted LLMs & Inference (Ollama, Mistral, etc.) | ✗ | ✓ | ✓ |
|
||||
| **MCP Gateway Capabilities** | | | |
|
||||
| Task-Based Access Control (TBAC) for AI Agents | ✗ | ✓ | ✓ |
|
||||
| MCP Servers Governance | ✗ | ✓ | ✓ |
|
||||
| Session-Smart Load Balancing for Agent Workflows | ✗ | ✓ | ✓ |
|
||||
| OAuth 2.1 / 2.0 Resource Server for MCP | ✗ | ✓ | ✓ |
|
||||
| Fine-grained Policy Enforcement for AI Tools | ✗ | ✓ | ✓ |
|
||||
| Audit-ready Observability for Agent Interactions | ✗ | ✓ | ✓ |
|
||||
| **API Management** | | | |
|
||||
| Flexible API grouping and versioning | ✗ | ✗ | ✓ |
|
||||
| API Developer Portal | ✗ | ✗ | ✓ |
|
||||
| OpenAPI Specifications Support | ✗ | ✗ | ✓ |
|
||||
| Multi-cluster dashboard | ✗ | ✗ | ✓ |
|
||||
| Built-in identity provider (or use your own) | ✗ | ✗ | ✓ |
|
||||
| Configuration linter & change impact analysis | ✗ | ✗ | ✓ |
|
||||
| Pre-built Grafana dashboards | ✗ | ✗ | ✓ |
|
||||
| Event correlation for quick incident mitigation | ✗ | ✗ | ✓ |
|
||||
| Traffic debugger | ✗ | ✓ | ✓ |
|
||||
| **Support** | | | |
|
||||
| Built-In Commercial Support | Add-on | ✓ | ✓ |
|
||||
|
||||
## Choosing the Right Product
|
||||
|
||||
### Start with Traefik Proxy
|
||||
|
||||
Traefik Proxy is the ideal starting point for organizations looking for a reliable, open-source application proxy with essential networking capabilities. Deploy it as your default ingress tier if you need:
|
||||
|
||||
- Basic reverse proxy and load balancing
|
||||
- Service discovery for containerized applications
|
||||
- Simple TLS termination and Let's Encrypt integration
|
||||
- Cost-effective solution with community support (can upgrade to Traefik Hub for more features)
|
||||
|
||||
### Upgrade to Traefik Hub API Gateway
|
||||
|
||||
Traefik Hub API Gateway layers enterprise security, distributed coordination, and AI Gateway capabilities on top of Traefik Proxy. Upgrade to it when you need:
|
||||
|
||||
- Enterprise security requirements (JWT, OIDC, LDAP)
|
||||
- Distributed deployments across multiple clusters
|
||||
- Advanced rate limiting and caching
|
||||
- WAF and policy enforcement
|
||||
- AI Gateway capabilities
|
||||
- Commercial support
|
||||
|
||||
### Consider Traefik AI Gateway
|
||||
|
||||
Traefik AI Gateway unifies hosted and self-hosted LLM access under centralized control and observability. Consider it if you have:
|
||||
|
||||
- Multi-LLM applications requiring unified API access
|
||||
- Organizations using multiple AI providers (OpenAI, Anthropic, Azure OpenAI, AWS Bedrock, etc.)
|
||||
- Local/self-hosted LLM deployments (Ollama, Mistral)
|
||||
- Centralized AI credential and security management
|
||||
- Cost optimization through semantic caching
|
||||
- PII protection and content filtering for AI interactions
|
||||
- Comprehensive AI observability and compliance requirements
|
||||
|
||||
### Choose Traefik MCP Gateway
|
||||
|
||||
Traefik MCP Gateway governs how AI agents interact with Model Context Protocol servers through task-aware policies and session-smart routing. Choose it if you need:
|
||||
|
||||
- AI agent deployments requiring secure access to MCP servers
|
||||
- Task-based access control (TBAC) for AI workflows
|
||||
- Governance of Model Context Protocol interactions
|
||||
- Session-smart routing for long-running agent conversations
|
||||
- OAuth 2.1 / 2.0 compliant MCP server protection
|
||||
- Audit-ready observability for AI agent activities
|
||||
- Fine-grained policy enforcement for AI tools and resources
|
||||
|
||||
### Choose Traefik Hub API Management
|
||||
|
||||
Traefik Hub API Management extends the gateway foundation with API lifecycle tooling, developer experience features, and governance workflows. Choose it when you have:
|
||||
|
||||
- Multiple APIs requiring centralized management
|
||||
- Developer teams needing self-service portals
|
||||
- Complex API versioning and lifecycle requirements
|
||||
- Multi-cluster environments requiring unified dashboards
|
||||
- Compliance and governance needs
|
||||
|
||||
## Migration Path
|
||||
|
||||
The Traefik ecosystem is designed for seamless upgrades. You can start with Traefik Proxy and add capabilities as your requirements grow:
|
||||
|
||||
1. **Traefik Proxy** → **Hub API Gateway**: Add enterprise security, distributed features, and AI Gateway capabilities
|
||||
2. **Hub API Gateway** → **Hub API Management**: Add comprehensive API management and governance features
|
||||
3. **MCP Gateway**: Specialized solution for AI agent governance and Model Context Protocol management
|
||||
|
||||
All products share the same core configuration concepts, making migration straightforward while preserving your existing configurations and operational knowledge.
|
||||
@@ -11,6 +11,8 @@ Traefik is an [open-source](https://github.com/traefik/traefik) Application Prox
|
||||
|
||||
If you start with Traefik for service discovery and routing, you can seamlessly add [API management](https://traefik.io/solutions/api-management/), [API gateway](https://traefik.io/solutions/api-gateway/), [AI gateway](https://traefik.io/solutions/ai-gateway/), and [API mocking](https://traefik.io/solutions/api-mocking/) capabilities as needed.
|
||||
|
||||
For a detailed comparison of all Traefik products and their capabilities, see our [Product Features Comparison](./features/).
|
||||
|
||||
With 3.3 billion downloads and over 55k stars on GitHub, Traefik is used globally across hybrid cloud, multi-cloud, on prem, and bare metal environments running Kubernetes, Docker Swarm, AWS, [the list goes on](https://doc.traefik.io/traefik/reference/install-configuration/providers/overview/).
|
||||
|
||||
Here’s how it works—Traefik receives requests on behalf of your system, identifies which components are responsible for handling them, and routes them securely. It automatically discovers the right configuration for your services by inspecting your infrastructure to identify relevant information and which service serves which request.
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: GatewayClass
|
||||
metadata:
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: GatewayClass
|
||||
metadata:
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
|
||||
@@ -3,13 +3,27 @@ title: "Traefik API & Dashboard Documentation"
|
||||
description: "Traefik Proxy exposes information through API handlers and showcase them on the Dashboard. Learn about the security, configuration, and endpoints of the APIs and Dashboard. Read the technical documentation."
|
||||
---
|
||||
|
||||
The dashboard is the central place that shows you the current active routes handled by Traefik.
|
||||
Traefik exposes a number of information through API endpoints, such as the configuration of your routers, services, middlewares, etc.
|
||||
|
||||
The dashboard, which is the central place that displays the current active routes handled by Traefik, fetches the data from this API.
|
||||
|
||||
<figure>
|
||||
<img src="../../../assets/img/webui-dashboard.png" alt="Dashboard - Providers" />
|
||||
<figcaption>The dashboard in action</figcaption>
|
||||
</figure>
|
||||
|
||||
## Security
|
||||
|
||||
Enabling the API and the dashboard in production is not recommended, because it will expose all configuration elements,
|
||||
including sensitive data, for which access should be reserved to administrators.
|
||||
|
||||
In production, it should be at least secured by authentication and authorizations.
|
||||
|
||||
!!! info
|
||||
|
||||
It's recommended to NOT publicly exposing the API's port, keeping it restricted to internal networks
|
||||
(as in the [principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege), applied to networks).
|
||||
|
||||
## Configuration Example
|
||||
|
||||
Enable the dashboard:
|
||||
@@ -187,6 +201,7 @@ All the following endpoints must be accessed with a `GET` HTTP request.
|
||||
| <a id="opt-apientrypoints" href="#opt-apientrypoints" title="#opt-apientrypoints">`/api/entrypoints`</a> | Lists all the entry points information. |
|
||||
| <a id="opt-apientrypointsname" href="#opt-apientrypointsname" title="#opt-apientrypointsname">`/api/entrypoints/{name}`</a> | Returns the information of the entry point specified by `name`. |
|
||||
| <a id="opt-apioverview" href="#opt-apioverview" title="#opt-apioverview">`/api/overview`</a> | Returns statistic information about HTTP, TCP and about enabled features and providers. |
|
||||
| <a id="opt-apisupport-dump" href="#opt-apisupport-dump" title="#opt-apisupport-dump">`/api/support-dump`</a> | Returns an archive that contains the anonymized static configuration and the runtime configuration. |
|
||||
| <a id="opt-apirawdata" href="#opt-apirawdata" title="#opt-apirawdata">`/api/rawdata`</a> | Returns information about dynamic configurations, errors, status and dependency relations. |
|
||||
| <a id="opt-apiversion" href="#opt-apiversion" title="#opt-apiversion">`/api/version`</a> | Returns information about Traefik version. |
|
||||
| <a id="opt-debugvars" href="#opt-debugvars" title="#opt-debugvars">`/debug/vars`</a> | See the [expvar](https://golang.org/pkg/expvar/) Go documentation. |
|
||||
@@ -203,14 +218,16 @@ All the following endpoints must be accessed with a `GET` HTTP request.
|
||||
|
||||
## Dashboard
|
||||
|
||||
The dashboard is available at the same location as the API, but by default on the path `/dashboard/`.
|
||||
The dashboard is available by default on the path `/dashboard/`.
|
||||
|
||||
!!! note
|
||||
|
||||
- The trailing slash `/` in `/dashboard/` is mandatory. This limitation can be mitigated using the the [RedirectRegex Middleware](../../middlewares/http/redirectregex.md).
|
||||
- There is also a redirect from the path `/` to `/dashboard/`, but you should not rely on this behavior, as it is subject to change and may complicate routing rules.
|
||||
- There is also a redirect from the path `/` to `/dashboard/`.
|
||||
|
||||
To securely access the dashboard, you need to define a routing configuration within Traefik. This involves setting up a router attached to the service `api@internal`, which allows you to:
|
||||
As mentioned above in the [Security](#security) section, it is important to secure access to both the dashboard and the API.
|
||||
You need to define a routing configuration within Traefik.
|
||||
This involves setting up a router attached to the service `api@internal`, which allows you to:
|
||||
|
||||
- Implement security features using [middlewares](../../middlewares/overview.md), such as authentication ([basicAuth](../../middlewares/http/basicauth.md), [digestAuth](../../middlewares/http/digestauth.md),
|
||||
[forwardAuth](../../middlewares/http/forwardauth.md)) or [allowlisting](../../middlewares/http/ipallowlist.md).
|
||||
|
||||
@@ -420,7 +420,7 @@ You can specify which Docker API Endpoint to use with the directive [`endpoint`]
|
||||
- [Traefik and Docker: A Discussion with Docker Captain, Bret Fisher](https://blog.traefik.io/traefik-and-docker-a-discussion-with-docker-captain-bret-fisher-7f0b9a54ff88)
|
||||
- [KubeCon EU 2018 Keynote, Running with Scissors, from Liz Rice](https://www.youtube.com/watch?v=ltrV-Qmh3oY)
|
||||
- [Don't expose the Docker socket (not even to a container)](https://www.lvh.io/posts/dont-expose-the-docker-socket-not-even-to-a-container/)
|
||||
- [A thread on Stack Overflow about sharing the `/var/run/docker.sock` file](https://news.ycombinator.com/item?id=17983623)
|
||||
- [A thread on Hacker News about sharing the `/var/run/docker.sock` file](https://news.ycombinator.com/item?id=17983623)
|
||||
- [To DinD or not to DinD](https://blog.loof.fr/2018/01/to-dind-or-not-do-dind.html)
|
||||
- [Traefik issue GH-4174 about security with Docker socket](https://github.com/traefik/traefik/issues/4174)
|
||||
- [Inspecting Docker Activity with Socat](https://developers.redhat.com/blog/2015/02/25/inspecting-docker-activity-with-socat/)
|
||||
|
||||
@@ -283,9 +283,11 @@ The following annotations are organized by category for easier navigation.
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioaffinity" href="#opt-nginx-ingress-kubernetes-ioaffinity" title="#opt-nginx-ingress-kubernetes-ioaffinity">`nginx.ingress.kubernetes.io/affinity`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-ioaffinity-mode" href="#opt-nginx-ingress-kubernetes-ioaffinity-mode" title="#opt-nginx-ingress-kubernetes-ioaffinity-mode">`nginx.ingress.kubernetes.io/affinity-mode`</a> | Only persistent mode supported; balanced/canary not supported. |
|
||||
| <a id="opt-nginx-ingress-kubernetes-iosession-cookie-name" href="#opt-nginx-ingress-kubernetes-iosession-cookie-name" title="#opt-nginx-ingress-kubernetes-iosession-cookie-name">`nginx.ingress.kubernetes.io/session-cookie-name`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-iosession-cookie-secure" href="#opt-nginx-ingress-kubernetes-iosession-cookie-secure" title="#opt-nginx-ingress-kubernetes-iosession-cookie-secure">`nginx.ingress.kubernetes.io/session-cookie-secure`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-iosession-cookie-path" href="#opt-nginx-ingress-kubernetes-iosession-cookie-path" title="#opt-nginx-ingress-kubernetes-iosession-cookie-path">`nginx.ingress.kubernetes.io/session-cookie-path`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-iosession-cookie-domain" href="#opt-nginx-ingress-kubernetes-iosession-cookie-domain" title="#opt-nginx-ingress-kubernetes-iosession-cookie-domain">`nginx.ingress.kubernetes.io/session-cookie-domain`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-iosession-cookie-samesite" href="#opt-nginx-ingress-kubernetes-iosession-cookie-samesite" title="#opt-nginx-ingress-kubernetes-iosession-cookie-samesite">`nginx.ingress.kubernetes.io/session-cookie-samesite`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-iosession-cookie-max-age" href="#opt-nginx-ingress-kubernetes-iosession-cookie-max-age" title="#opt-nginx-ingress-kubernetes-iosession-cookie-max-age">`nginx.ingress.kubernetes.io/session-cookie-max-age`</a> | |
|
||||
|
||||
### Load Balancing & Backend
|
||||
|
||||
@@ -304,6 +306,7 @@ The following annotations are organized by category for easier navigation.
|
||||
| <a id="opt-nginx-ingress-kubernetes-iocors-allow-headers" href="#opt-nginx-ingress-kubernetes-iocors-allow-headers" title="#opt-nginx-ingress-kubernetes-iocors-allow-headers">`nginx.ingress.kubernetes.io/cors-allow-headers`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-iocors-allow-methods" href="#opt-nginx-ingress-kubernetes-iocors-allow-methods" title="#opt-nginx-ingress-kubernetes-iocors-allow-methods">`nginx.ingress.kubernetes.io/cors-allow-methods`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-iocors-allow-origin" href="#opt-nginx-ingress-kubernetes-iocors-allow-origin" title="#opt-nginx-ingress-kubernetes-iocors-allow-origin">`nginx.ingress.kubernetes.io/cors-allow-origin`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-iocors-expose-headers" href="#opt-nginx-ingress-kubernetes-iocors-expose-headers" title="#opt-nginx-ingress-kubernetes-iocors-expose-headers">`nginx.ingress.kubernetes.io/cors-expose-headers`</a> | |
|
||||
| <a id="opt-nginx-ingress-kubernetes-iocors-max-age" href="#opt-nginx-ingress-kubernetes-iocors-max-age" title="#opt-nginx-ingress-kubernetes-iocors-max-age">`nginx.ingress.kubernetes.io/cors-max-age`</a> | |
|
||||
|
||||
### Routing
|
||||
|
||||
@@ -126,7 +126,7 @@ ingressRoute:
|
||||
middlewares:
|
||||
- name: dashboard-auth
|
||||
|
||||
# Creates a BasiAuth Middleware and Secret for the Dashboard Security
|
||||
# Creates a BasicAuth Middleware and Secret for the Dashboard Security
|
||||
extraObjects:
|
||||
- apiVersion: v1
|
||||
kind: Secret
|
||||
|
||||
@@ -61,7 +61,7 @@ In the same directory, create `docker‑compose‑swarm.yaml`:
|
||||
```yaml
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v3.4
|
||||
image: traefik:v3.6
|
||||
|
||||
networks:
|
||||
# Connect to the 'traefik_proxy' overlay network for inter-container communication across nodes
|
||||
|
||||
@@ -2,24 +2,27 @@ site_name: Traefik
|
||||
site_description: Traefik Documentation
|
||||
site_author: traefik.io
|
||||
site_url: https://doc.traefik.io/traefik
|
||||
dev_addr: 0.0.0.0:8000
|
||||
dev_addr: localhost:8000
|
||||
|
||||
repo_name: 'GitHub'
|
||||
repo_url: 'https://github.com/traefik/traefik'
|
||||
|
||||
docs_dir: 'content'
|
||||
|
||||
product: proxy
|
||||
|
||||
# https://squidfunk.github.io/mkdocs-material/
|
||||
# Use custom version of mkdocs-material
|
||||
# See https://github.com/traefik/mkdocs-material
|
||||
theme:
|
||||
name: 'traefik-labs'
|
||||
product: proxy
|
||||
language: en
|
||||
include_sidebar: true
|
||||
favicon: assets/img/traefikproxy-icon-color.png
|
||||
logo: assets/img/traefikproxy-vertical-logo-color.svg
|
||||
feature:
|
||||
tabs: false
|
||||
features:
|
||||
- content.code.copy
|
||||
palette:
|
||||
primary: 'cyan'
|
||||
accent: 'cyan'
|
||||
@@ -27,7 +30,7 @@ theme:
|
||||
prev: 'Previous'
|
||||
next: 'Next'
|
||||
|
||||
copyright: 'Traefik Labs • Copyright © 2016-2025'
|
||||
copyright: 'Traefik Labs • Copyright © 2016-2026'
|
||||
|
||||
extra_javascript:
|
||||
- assets/js/hljs/highlight.pack.js # Download from https://highlightjs.org/download/ and enable YAML, TOML and Dockerfile
|
||||
@@ -35,6 +38,7 @@ extra_javascript:
|
||||
|
||||
extra_css:
|
||||
- assets/css/menu-icons.css
|
||||
- assets/css/code-copy.css
|
||||
|
||||
plugins:
|
||||
- search
|
||||
@@ -185,6 +189,7 @@ markdown_extensions:
|
||||
# Page tree
|
||||
nav:
|
||||
- 'What is Traefik': 'index.md'
|
||||
- 'Features': 'features/index.md'
|
||||
- 'Getting Started':
|
||||
- 'Overview': 'getting-started/index.md'
|
||||
- 'Configuration Introduction': 'getting-started/configuration-overview.md'
|
||||
|
||||
@@ -16,3 +16,15 @@
|
||||
|
||||
[pymdown-extensions]: https://facelessuser.github.io/pymdown-extensions "PyMdown Extensions"
|
||||
[pymdown-extensions-src]: https://github.com/facelessuser/pymdown-extensions "PyMdown Extensions - Sources"
|
||||
|
||||
## Build locally without docker
|
||||
|
||||
```sh
|
||||
# Pre-requisite: python3, pip and virtualenv
|
||||
DOCS="/tmp/traefik-docs"
|
||||
mkdir "$DOCS"
|
||||
virtualenv "$DOCS"
|
||||
source "$DOCS/bin/activate"
|
||||
pip install -r requirements.txt
|
||||
mkdocs serve # or mkdocs build
|
||||
```
|
||||
|
||||
48
docs/scripts/lint-yaml.sh
Executable file
48
docs/scripts/lint-yaml.sh
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/bin/sh
|
||||
# This script checks that YAML files with multiple Kubernetes resources
|
||||
# do not start with '---'
|
||||
#
|
||||
# Rule: If a YAML file contains more than one Kubernetes resource
|
||||
# (indicated by '---' separator in the middle of the file),
|
||||
# it should NOT start with '---'
|
||||
|
||||
set -eu
|
||||
|
||||
BASE_DIR="${1:-/app}"
|
||||
|
||||
echo "== Linting YAML files (Kubernetes multi-resource format)"
|
||||
|
||||
# Find all YAML files in the content directory
|
||||
find "${BASE_DIR}/content" -type f \( -name "*.yml" -o -name "*.yaml" \) | while read -r file; do
|
||||
# Count the number of '---' lines in the file
|
||||
separator_count=$(grep -c "^---" "$file" || true)
|
||||
|
||||
# Check if file starts with '---'
|
||||
starts_with_separator=false
|
||||
if head -1 "$file" | grep -q "^---"; then
|
||||
starts_with_separator=true
|
||||
fi
|
||||
|
||||
# If file has multiple resources (separator_count >= 1 when starting with ---, or >= 2 otherwise)
|
||||
# and starts with '---', it's an error
|
||||
#
|
||||
# Logic:
|
||||
# - If starts with '---' and has more than 1 separator -> multiple resources, error
|
||||
# - If doesn't start with '---' and has 1+ separators -> multiple resources, ok
|
||||
if [ "$starts_with_separator" = true ] && [ "$separator_count" -gt 1 ]; then
|
||||
echo "ERROR: $file starts with '---' but contains multiple Kubernetes resources"
|
||||
echo " Files with multiple resources should not start with '---'"
|
||||
# We need to signal error but can't use EXIT_CODE in subshell
|
||||
# So we output to a temp file
|
||||
echo "1" > /tmp/yaml_lint_error
|
||||
fi
|
||||
done
|
||||
|
||||
# Check if any errors were found
|
||||
if [ -f /tmp/yaml_lint_error ]; then
|
||||
rm -f /tmp/yaml_lint_error
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "YAML lint passed"
|
||||
exit 0
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
# This script will run a couple of linter on the documentation
|
||||
|
||||
set -eu
|
||||
@@ -6,14 +6,17 @@ set -eu
|
||||
# We want to run all linters before returning success (exit 0) or failure (exit 1)
|
||||
# So this variable holds the global exit code
|
||||
EXIT_CODE=0
|
||||
readonly BASE_DIR=/app
|
||||
readonly BASE_DIR="${1:-/app}"
|
||||
|
||||
# Run YAML linter for Kubernetes multi-resource files
|
||||
./docs/scripts/lint-yaml.sh "${BASE_DIR}" || EXIT_CODE=1
|
||||
|
||||
echo "== Linting Markdown"
|
||||
# Uses the file ".markdownlint.json" for setup
|
||||
cd "${BASE_DIR}" || exit 1
|
||||
|
||||
LINTER_EXCLUSIONS="$(find "${BASE_DIR}/content" -type f -name '.markdownlint.json')"
|
||||
GLOBAL_LINT_OPTIONS="--config ${BASE_DIR}/.markdownlint.json"
|
||||
LINTER_EXCLUSIONS="$(find "content" -type f -name '.markdownlint.json')"
|
||||
GLOBAL_LINT_OPTIONS="--config .markdownlint.json"
|
||||
|
||||
# Lint the specific folders (containing linter specific rulesets)
|
||||
for LINTER_EXCLUSION in ${LINTER_EXCLUSIONS}
|
||||
@@ -24,6 +27,6 @@ do
|
||||
done
|
||||
|
||||
# Lint all the content, excluding the previously done`
|
||||
eval markdownlint "${GLOBAL_LINT_OPTIONS}" "${BASE_DIR}/content/**/*.md" || EXIT_CODE=1
|
||||
eval markdownlint "${GLOBAL_LINT_OPTIONS}" "content/**/*.md" || EXIT_CODE=1
|
||||
|
||||
exit "${EXIT_CODE}"
|
||||
|
||||
@@ -79,7 +79,13 @@ func Append(router *mux.Router, basePath string, customAssets fs.FS) error {
|
||||
router.Methods(http.MethodGet).
|
||||
Path(basePath).
|
||||
HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
prefix := strings.TrimSuffix(req.Header.Get("X-Forwarded-Prefix"), "/")
|
||||
xfPrefix := req.Header.Get("X-Forwarded-Prefix")
|
||||
if strings.Contains(xfPrefix, "//") {
|
||||
log.Error().Msgf("X-Forwarded-Prefix contains an invalid value: %s, defaulting to empty prefix", xfPrefix)
|
||||
xfPrefix = ""
|
||||
}
|
||||
|
||||
prefix := strings.TrimSuffix(xfPrefix, "/")
|
||||
http.Redirect(resp, req, prefix+dashboardPath, http.StatusFound)
|
||||
})
|
||||
|
||||
|
||||
@@ -9,7 +9,9 @@ import (
|
||||
"testing/fstest"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_ContentSecurityPolicy(t *testing.T) {
|
||||
@@ -60,6 +62,52 @@ func Test_ContentSecurityPolicy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_XForwardedPrefix(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
prefix string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "location in X-Forwarded-Prefix",
|
||||
prefix: "//foobar/test",
|
||||
expected: "/dashboard/",
|
||||
},
|
||||
{
|
||||
desc: "scheme in X-Forwarded-Prefix",
|
||||
prefix: "http://foobar",
|
||||
expected: "/dashboard/",
|
||||
},
|
||||
{
|
||||
desc: "path in X-Forwarded-Prefix",
|
||||
prefix: "foobar",
|
||||
expected: "/foobar/dashboard/",
|
||||
},
|
||||
}
|
||||
|
||||
router := mux.NewRouter()
|
||||
err := Append(router, "/", fstest.MapFS{"index.html": &fstest.MapFile{
|
||||
Mode: 0o755,
|
||||
ModTime: time.Now(),
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/", http.NoBody)
|
||||
req.Header.Set("X-Forwarded-Prefix", test.prefix)
|
||||
rw := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(rw, req)
|
||||
|
||||
assert.Equal(t, http.StatusFound, rw.Code)
|
||||
assert.Equal(t, test.expected, rw.Result().Header.Get("Location"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type errorFS struct{}
|
||||
|
||||
func (e errorFS) Open(name string) (fs.File, error) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -314,6 +314,18 @@ func (c *Configuration) SetEffectiveConfiguration() {
|
||||
c.Providers.KubernetesGateway.EntryPoints = entryPoints
|
||||
}
|
||||
|
||||
// Configure Ingress NGINX provider.
|
||||
if c.Providers.KubernetesIngressNGINX != nil {
|
||||
var nonTLSEntryPoints []string
|
||||
for epName, entryPoint := range c.EntryPoints {
|
||||
if entryPoint.HTTP.TLS == nil {
|
||||
nonTLSEntryPoints = append(nonTLSEntryPoints, epName)
|
||||
}
|
||||
}
|
||||
|
||||
c.Providers.KubernetesIngressNGINX.NonTLSEntryPoints = nonTLSEntryPoints
|
||||
}
|
||||
|
||||
// Defines the default rule syntax for the Kubernetes Ingress Provider.
|
||||
// This allows the provider to adapt the matcher syntax to the desired rule syntax version.
|
||||
if c.Core != nil && c.Providers.KubernetesIngress != nil {
|
||||
|
||||
@@ -921,11 +921,11 @@ func (p *Provider) renewCertificates(ctx context.Context, renewPeriod time.Durat
|
||||
for _, cert := range certificates {
|
||||
client, err := p.getClient()
|
||||
if err != nil {
|
||||
logger.Info().Err(err).Msgf("Error renewing certificate from LE : %+v", cert.Domain)
|
||||
logger.Info().Err(err).Msgf("Error renewing ACME certificate: %+v", cert.Domain)
|
||||
continue
|
||||
}
|
||||
|
||||
logger.Info().Msgf("Renewing certificate from LE : %+v", cert.Domain)
|
||||
logger.Info().Msgf("Renewing ACME certificate: %+v", cert.Domain)
|
||||
|
||||
res := certificate.Resource{
|
||||
Domain: cert.Domain.Main,
|
||||
@@ -935,12 +935,14 @@ func (p *Provider) renewCertificates(ctx context.Context, renewPeriod time.Durat
|
||||
|
||||
opts := &certificate.RenewOptions{
|
||||
Bundle: true,
|
||||
EmailAddresses: p.EmailAddresses,
|
||||
Profile: p.Profile,
|
||||
PreferredChain: p.PreferredChain,
|
||||
}
|
||||
|
||||
renewedCert, err := client.Certificate.RenewWithOptions(res, opts)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msgf("Error renewing certificate from LE: %v", cert.Domain)
|
||||
logger.Error().Err(err).Msgf("Error renewing ACME certificate: %v", cert.Domain)
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: ingress-with-no-annotation
|
||||
namespace: default
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: whoami.localhost
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
service:
|
||||
name: whoami
|
||||
port:
|
||||
number: 80
|
||||
path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- hosts:
|
||||
- whoami.localhost
|
||||
secretName: whoami-tls
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: ingress-with-use-regex
|
||||
namespace: default
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/use-regex: "true"
|
||||
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: use-regex.localhost
|
||||
http:
|
||||
paths:
|
||||
- path: /test(.*)
|
||||
pathType: ImplementationSpecific
|
||||
backend:
|
||||
service:
|
||||
name: whoami
|
||||
port:
|
||||
number: 80
|
||||
@@ -80,6 +80,9 @@ type Provider struct {
|
||||
DefaultBackendService string `description:"Service used to serve HTTP requests not matching any known server name (catch-all). Takes the form 'namespace/name'." json:"defaultBackendService,omitempty" toml:"defaultBackendService,omitempty" yaml:"defaultBackendService,omitempty" export:"true"`
|
||||
DisableSvcExternalName bool `description:"Disable support for Services of type ExternalName." json:"disableSvcExternalName,omitempty" toml:"disableSvcExternalName,omitempty" yaml:"disableSvcExternalName,omitempty" export:"true"`
|
||||
|
||||
// NonTLSEntryPoints contains the names of entrypoints that are configured without TLS.
|
||||
NonTLSEntryPoints []string `json:"-" toml:"-" yaml:"-" label:"-" file:"-"`
|
||||
|
||||
defaultBackendServiceNamespace string
|
||||
defaultBackendServiceName string
|
||||
|
||||
@@ -798,7 +801,7 @@ func (p *Provider) applyMiddlewares(namespace, routerKey string, ingressConfig i
|
||||
|
||||
// Apply SSL redirect is mandatory to be applied after all other middlewares.
|
||||
// TODO: check how to remove this, and create the HTTP router elsewhere.
|
||||
applySSLRedirectConfiguration(routerKey, ingressConfig, hasTLS, rt, conf)
|
||||
p.applySSLRedirectConfiguration(routerKey, ingressConfig, hasTLS, rt, conf)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -934,7 +937,7 @@ func applyCORSConfiguration(routerName string, ingressConfig ingressConfig, rt *
|
||||
rt.Middlewares = append(rt.Middlewares, corsMiddlewareName)
|
||||
}
|
||||
|
||||
func applySSLRedirectConfiguration(routerName string, ingressConfig ingressConfig, hasTLS bool, rt *dynamic.Router, conf *dynamic.Configuration) {
|
||||
func (p *Provider) applySSLRedirectConfiguration(routerName string, ingressConfig ingressConfig, hasTLS bool, rt *dynamic.Router, conf *dynamic.Configuration) {
|
||||
var forceSSLRedirect bool
|
||||
if ingressConfig.ForceSSLRedirect != nil {
|
||||
forceSSLRedirect = *ingressConfig.ForceSSLRedirect
|
||||
@@ -946,7 +949,9 @@ func applySSLRedirectConfiguration(routerName string, ingressConfig ingressConfi
|
||||
// An Ingress with TLS configuration creates only a Traefik router with a TLS configuration,
|
||||
// so no Non-TLS router exists to handle HTTP traffic, and we should create it.
|
||||
httpRouter := &dynamic.Router{
|
||||
Rule: rt.Rule,
|
||||
// Only attach to entryPoint which do not activate TLS.
|
||||
EntryPoints: p.NonTLSEntryPoints,
|
||||
Rule: rt.Rule,
|
||||
// "default" stands for the default rule syntax in Traefik v3, i.e. the v3 syntax.
|
||||
RuleSyntax: "default",
|
||||
Middlewares: rt.Middlewares,
|
||||
@@ -1060,7 +1065,7 @@ func buildRule(host string, pa netv1.HTTPIngressPath, config ingressConfig) stri
|
||||
rules = append(rules, fmt.Sprintf("Path(`%s`)", pa.Path))
|
||||
case netv1.PathTypePrefix:
|
||||
if ptr.Deref(config.UseRegex, false) {
|
||||
rules = append(rules, fmt.Sprintf("PathRegexp(`^%s`)", regexp.QuoteMeta(pa.Path)))
|
||||
rules = append(rules, fmt.Sprintf("PathRegexp(`^%s`)", pa.Path))
|
||||
} else {
|
||||
rules = append(rules, buildPrefixRule(pa.Path))
|
||||
}
|
||||
|
||||
@@ -46,6 +46,76 @@ func TestLoadIngresses(t *testing.T) {
|
||||
TLS: &dynamic.TLSConfiguration{},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "No annotation",
|
||||
paths: []string{
|
||||
"ingresses/00-ingress-with-no-annotation.yml",
|
||||
"ingressclasses.yml",
|
||||
"services.yml",
|
||||
"secrets.yml",
|
||||
},
|
||||
expected: &dynamic.Configuration{
|
||||
TCP: &dynamic.TCPConfiguration{
|
||||
Routers: map[string]*dynamic.TCPRouter{},
|
||||
Services: map[string]*dynamic.TCPService{},
|
||||
},
|
||||
HTTP: &dynamic.HTTPConfiguration{
|
||||
Routers: map[string]*dynamic.Router{
|
||||
"default-ingress-with-no-annotation-rule-0-path-0": {
|
||||
Rule: "Host(`whoami.localhost`) && PathPrefix(`/`)",
|
||||
RuleSyntax: "default",
|
||||
TLS: &dynamic.RouterTLSConfig{},
|
||||
Service: "default-ingress-with-no-annotation-whoami-80",
|
||||
},
|
||||
"default-ingress-with-no-annotation-rule-0-path-0-http": {
|
||||
EntryPoints: []string{"web"},
|
||||
Rule: "Host(`whoami.localhost`) && PathPrefix(`/`)",
|
||||
RuleSyntax: "default",
|
||||
Middlewares: []string{"default-ingress-with-no-annotation-rule-0-path-0-redirect-scheme"},
|
||||
Service: "noop@internal",
|
||||
},
|
||||
},
|
||||
Middlewares: map[string]*dynamic.Middleware{
|
||||
"default-ingress-with-no-annotation-rule-0-path-0-redirect-scheme": {
|
||||
RedirectScheme: &dynamic.RedirectScheme{
|
||||
Scheme: "https",
|
||||
ForcePermanentRedirect: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Services: map[string]*dynamic.Service{
|
||||
"default-ingress-with-no-annotation-whoami-80": {
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://10.10.0.1:80",
|
||||
},
|
||||
{
|
||||
URL: "http://10.10.0.2:80",
|
||||
},
|
||||
},
|
||||
Strategy: "wrr",
|
||||
PassHostHeader: ptr.To(true),
|
||||
ResponseForwarding: &dynamic.ResponseForwarding{
|
||||
FlushInterval: dynamic.DefaultFlushInterval,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ServersTransports: map[string]*dynamic.ServersTransport{},
|
||||
},
|
||||
TLS: &dynamic.TLSConfiguration{
|
||||
Certificates: []*tls.CertAndStores{
|
||||
{
|
||||
Certificate: tls.Certificate{
|
||||
CertFile: "-----BEGIN CERTIFICATE-----",
|
||||
KeyFile: "-----BEGIN CERTIFICATE-----",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Basic Auth",
|
||||
paths: []string{
|
||||
@@ -176,15 +246,17 @@ func TestLoadIngresses(t *testing.T) {
|
||||
Service: "default-ingress-with-ssl-redirect-whoami-80",
|
||||
},
|
||||
"default-ingress-with-ssl-redirect-rule-0-path-0-http": {
|
||||
EntryPoints: []string{"web"},
|
||||
Rule: "Host(`sslredirect.localhost`) && Path(`/`)",
|
||||
RuleSyntax: "default",
|
||||
Middlewares: []string{"default-ingress-with-ssl-redirect-rule-0-path-0-redirect-scheme"},
|
||||
Service: "noop@internal",
|
||||
},
|
||||
"default-ingress-without-ssl-redirect-rule-0-path-0-http": {
|
||||
Rule: "Host(`withoutsslredirect.localhost`) && Path(`/`)",
|
||||
RuleSyntax: "default",
|
||||
Service: "default-ingress-without-ssl-redirect-whoami-80",
|
||||
EntryPoints: []string{"web"},
|
||||
Rule: "Host(`withoutsslredirect.localhost`) && Path(`/`)",
|
||||
RuleSyntax: "default",
|
||||
Service: "default-ingress-without-ssl-redirect-whoami-80",
|
||||
},
|
||||
"default-ingress-without-ssl-redirect-rule-0-path-0": {
|
||||
Rule: "Host(`withoutsslredirect.localhost`) && Path(`/`)",
|
||||
@@ -533,6 +605,51 @@ func TestLoadIngresses(t *testing.T) {
|
||||
TLS: &dynamic.TLSConfiguration{},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Use Regex",
|
||||
paths: []string{
|
||||
"services.yml",
|
||||
"ingressclasses.yml",
|
||||
"ingresses/10-ingress-with-use-regex.yml",
|
||||
},
|
||||
expected: &dynamic.Configuration{
|
||||
TCP: &dynamic.TCPConfiguration{
|
||||
Routers: map[string]*dynamic.TCPRouter{},
|
||||
Services: map[string]*dynamic.TCPService{},
|
||||
},
|
||||
HTTP: &dynamic.HTTPConfiguration{
|
||||
Routers: map[string]*dynamic.Router{
|
||||
"default-ingress-with-use-regex-rule-0-path-0": {
|
||||
Rule: "Host(`use-regex.localhost`) && PathRegexp(`^/test(.*)`)",
|
||||
RuleSyntax: "default",
|
||||
Service: "default-ingress-with-use-regex-whoami-80",
|
||||
},
|
||||
},
|
||||
Middlewares: map[string]*dynamic.Middleware{},
|
||||
Services: map[string]*dynamic.Service{
|
||||
"default-ingress-with-use-regex-whoami-80": {
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://10.10.0.1:80",
|
||||
},
|
||||
{
|
||||
URL: "http://10.10.0.2:80",
|
||||
},
|
||||
},
|
||||
Strategy: "wrr",
|
||||
PassHostHeader: ptr.To(true),
|
||||
ResponseForwarding: &dynamic.ResponseForwarding{
|
||||
FlushInterval: dynamic.DefaultFlushInterval,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ServersTransports: map[string]*dynamic.ServersTransport{},
|
||||
},
|
||||
TLS: &dynamic.TLSConfiguration{},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Default Backend",
|
||||
defaultBackendServiceName: "whoami",
|
||||
@@ -608,6 +725,7 @@ func TestLoadIngresses(t *testing.T) {
|
||||
k8sClient: client,
|
||||
defaultBackendServiceName: test.defaultBackendServiceName,
|
||||
defaultBackendServiceNamespace: test.defaultBackendServiceNamespace,
|
||||
NonTLSEntryPoints: []string{"web"},
|
||||
}
|
||||
p.SetDefaults()
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
kind: Ingress
|
||||
apiVersion: networking.k8s.io/v1
|
||||
metadata:
|
||||
name: defaultbackend
|
||||
namespace: testing
|
||||
|
||||
spec:
|
||||
defaultBackend:
|
||||
resource:
|
||||
apiGroup: example.com
|
||||
kind: SomeBackend
|
||||
name: foo
|
||||
@@ -0,0 +1,8 @@
|
||||
kind: Ingress
|
||||
apiVersion: networking.k8s.io/v1
|
||||
metadata:
|
||||
name: defaultbackend
|
||||
namespace: testing
|
||||
|
||||
spec:
|
||||
defaultBackend: {}
|
||||
@@ -269,6 +269,17 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
|
||||
continue
|
||||
}
|
||||
|
||||
if ingress.Spec.DefaultBackend.Resource != nil {
|
||||
// https://kubernetes.io/docs/concepts/services-networking/ingress/#resource-backend
|
||||
logger.Error().Msg("Resource is not supported for default backend")
|
||||
continue
|
||||
}
|
||||
|
||||
if ingress.Spec.DefaultBackend.Service == nil {
|
||||
logger.Error().Msg("Default backend is missing service definition")
|
||||
continue
|
||||
}
|
||||
|
||||
service, err := p.loadService(client, ingress.Namespace, *ingress.Spec.DefaultBackend)
|
||||
if err != nil {
|
||||
logger.Error().
|
||||
|
||||
@@ -550,6 +550,26 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Ingress with defaultbackend with resource",
|
||||
expected: &dynamic.Configuration{
|
||||
HTTP: &dynamic.HTTPConfiguration{
|
||||
Middlewares: map[string]*dynamic.Middleware{},
|
||||
Routers: map[string]*dynamic.Router{},
|
||||
Services: map[string]*dynamic.Service{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Ingress with empty defaultbackend",
|
||||
expected: &dynamic.Configuration{
|
||||
HTTP: &dynamic.HTTPConfiguration{
|
||||
Middlewares: map[string]*dynamic.Middleware{},
|
||||
Routers: map[string]*dynamic.Router{},
|
||||
Services: map[string]*dynamic.Service{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Ingress with one service without endpoint",
|
||||
expected: &dynamic.Configuration{
|
||||
|
||||
@@ -3,6 +3,7 @@ package tcp
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"io"
|
||||
@@ -222,7 +223,17 @@ func (r *Router) acmeTLSALPNHandler() tcp.Handler {
|
||||
}
|
||||
|
||||
return tcp.HandlerFunc(func(conn tcp.WriteCloser) {
|
||||
_ = tls.Server(conn, r.httpsTLSConfig).Handshake()
|
||||
tlsConn := tls.Server(conn, r.httpsTLSConfig)
|
||||
defer tlsConn.Close()
|
||||
|
||||
// This avoids stale connections when validating the ACME challenge,
|
||||
// as we expect a validation request to complete in a short period of time.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := tlsConn.HandshakeContext(ctx); err != nil {
|
||||
log.Debug().Err(err).Msg("Error during ACME-TLS/1 handshake")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -697,6 +697,64 @@ func Test_Routing(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Router_acmeTLSALPNHandlerTimeout(t *testing.T) {
|
||||
router, err := NewRouter()
|
||||
require.NoError(t, err)
|
||||
|
||||
router.httpsTLSConfig = &tls.Config{}
|
||||
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
require.NoError(t, err)
|
||||
|
||||
acceptCh := make(chan struct{}, 1)
|
||||
go func() {
|
||||
close(acceptCh)
|
||||
|
||||
conn, err := listener.Accept()
|
||||
require.NoError(t, err)
|
||||
|
||||
defer listener.Close()
|
||||
|
||||
router.acmeTLSALPNHandler().
|
||||
ServeTCP(conn.(*net.TCPConn))
|
||||
}()
|
||||
|
||||
<-acceptCh
|
||||
|
||||
conn, err := net.DialTimeout("tcp", listener.Addr().String(), 2*time.Second)
|
||||
require.NoError(t, err)
|
||||
|
||||
// This is a minimal truncated Client Hello message
|
||||
// to simulate a hanging connection during TLS handshake.
|
||||
clientHello := []byte{
|
||||
// TLS Record Header
|
||||
0x16, // Content Type: Handshake
|
||||
0x03, 0x01, // Version: TLS 1.0 (for compatibility)
|
||||
0x00, 0x50, // Length: 80 bytes
|
||||
}
|
||||
|
||||
_, err = conn.Write(clientHello)
|
||||
require.NoError(t, err)
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
// This will return an EOF as the acmeTLSALPNHandler will close the connection
|
||||
// after a timeout during the TLS handshake.
|
||||
b := make([]byte, 256)
|
||||
_, err = conn.Read(b)
|
||||
|
||||
errCh <- err
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-errCh:
|
||||
assert.ErrorIs(t, err, io.EOF)
|
||||
|
||||
case <-time.After(3 * time.Second):
|
||||
t.Fatal("Error: Timeout waiting for acmeTLSALPNHandler to close the connection")
|
||||
}
|
||||
}
|
||||
|
||||
// routerTCPCatchAll configures a TCP CatchAll No TLS - HostSNI(`*`) router.
|
||||
func routerTCPCatchAll(conf *runtime.Configuration) {
|
||||
conf.TCPRouters["tcp-catchall"] = &runtime.TCPRouterInfo{
|
||||
|
||||
@@ -12,11 +12,16 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// newTestRand creates a deterministic random source for reproducible tests.
|
||||
func newTestRand() *rand.Rand {
|
||||
return rand.New(rand.NewSource(12345))
|
||||
}
|
||||
|
||||
// genIPAddress generate randomly an IP address as a string.
|
||||
func genIPAddress() string {
|
||||
func genIPAddress(rng *rand.Rand) string {
|
||||
buf := make([]byte, 4)
|
||||
|
||||
ip := rand.Uint32()
|
||||
ip := rng.Uint32()
|
||||
|
||||
binary.LittleEndian.PutUint32(buf, ip)
|
||||
ipStr := net.IP(buf)
|
||||
@@ -37,6 +42,7 @@ func initStatusArray(size int, value int) []int {
|
||||
// The tests validate repartition using a margin of 10% of the number of requests
|
||||
|
||||
func TestBalancer(t *testing.T) {
|
||||
rng := newTestRand()
|
||||
balancer := New(false)
|
||||
|
||||
balancer.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
@@ -52,7 +58,7 @@ func TestBalancer(t *testing.T) {
|
||||
recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}}
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
for range 100 {
|
||||
req.RemoteAddr = genIPAddress()
|
||||
req.RemoteAddr = genIPAddress(rng)
|
||||
balancer.ServeHTTP(recorder, req)
|
||||
}
|
||||
assert.InDelta(t, 80, recorder.save["first"], 10)
|
||||
@@ -132,6 +138,7 @@ func TestBalancerOneServerDown(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBalancerDownThenUp(t *testing.T) {
|
||||
rng := newTestRand()
|
||||
balancer := New(false)
|
||||
|
||||
balancer.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
@@ -155,7 +162,7 @@ func TestBalancerDownThenUp(t *testing.T) {
|
||||
recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}}
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
for range 100 {
|
||||
req.RemoteAddr = genIPAddress()
|
||||
req.RemoteAddr = genIPAddress(rng)
|
||||
balancer.ServeHTTP(recorder, req)
|
||||
}
|
||||
assert.InDelta(t, 50, recorder.save["first"], 10)
|
||||
@@ -163,6 +170,7 @@ func TestBalancerDownThenUp(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBalancerPropagate(t *testing.T) {
|
||||
rng := newTestRand()
|
||||
balancer1 := New(true)
|
||||
|
||||
balancer1.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
@@ -188,8 +196,6 @@ func TestBalancerPropagate(t *testing.T) {
|
||||
topBalancer.Add("balancer1", balancer1, Int(1), false)
|
||||
_ = balancer1.RegisterStatusUpdater(func(up bool) {
|
||||
topBalancer.SetStatus(context.WithValue(t.Context(), serviceName, "top"), "balancer1", up)
|
||||
// TODO(mpl): if test gets flaky, add channel or something here to signal that
|
||||
// propagation is done, and wait on it before sending request.
|
||||
})
|
||||
topBalancer.Add("balancer2", balancer2, Int(1), false)
|
||||
_ = balancer2.RegisterStatusUpdater(func(up bool) {
|
||||
@@ -199,7 +205,7 @@ func TestBalancerPropagate(t *testing.T) {
|
||||
recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}}
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
for range 100 {
|
||||
req.RemoteAddr = genIPAddress()
|
||||
req.RemoteAddr = genIPAddress(rng)
|
||||
topBalancer.ServeHTTP(recorder, req)
|
||||
}
|
||||
assert.InDelta(t, 25, recorder.save["first"], 10)
|
||||
@@ -214,7 +220,7 @@ func TestBalancerPropagate(t *testing.T) {
|
||||
recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}}
|
||||
req = httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
for range 100 {
|
||||
req.RemoteAddr = genIPAddress()
|
||||
req.RemoteAddr = genIPAddress(rng)
|
||||
topBalancer.ServeHTTP(recorder, req)
|
||||
}
|
||||
assert.InDelta(t, 25, recorder.save["first"], 10)
|
||||
@@ -230,7 +236,7 @@ func TestBalancerPropagate(t *testing.T) {
|
||||
recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}}
|
||||
req = httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
for range 100 {
|
||||
req.RemoteAddr = genIPAddress()
|
||||
req.RemoteAddr = genIPAddress(rng)
|
||||
topBalancer.ServeHTTP(recorder, req)
|
||||
}
|
||||
assert.InDelta(t, 50, recorder.save["first"], 10)
|
||||
@@ -254,6 +260,7 @@ func TestBalancerAllServersZeroWeight(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSticky(t *testing.T) {
|
||||
rng := newTestRand()
|
||||
balancer := New(false)
|
||||
|
||||
balancer.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
@@ -269,7 +276,7 @@ func TestSticky(t *testing.T) {
|
||||
recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}}
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
req.RemoteAddr = genIPAddress()
|
||||
req.RemoteAddr = genIPAddress(rng)
|
||||
for range 10 {
|
||||
for _, cookie := range recorder.Result().Cookies() {
|
||||
req.AddCookie(cookie)
|
||||
|
||||
@@ -972,23 +972,22 @@ func TestTrafficShiftsWhenPerformanceDegrades(t *testing.T) {
|
||||
assert.InDelta(t, 25, recorder.save["server2"], 10) // 25 ± 10 requests
|
||||
|
||||
// Phase 2: server1 degrades (simulating GC pause, CPU spike, or network latency).
|
||||
server1Delay.Store(15) // Now 15ms (3x slower)
|
||||
server1Delay.Store(50) // Now 50ms (10x slower) - dramatic degradation for reliable detection
|
||||
|
||||
// Make more requests to shift the moving average.
|
||||
// Ring buffer has 100 samples, need significant new samples to shift average.
|
||||
// server1's average will climb from ~5ms toward 15ms.
|
||||
// server1's average will climb from ~5ms toward 50ms.
|
||||
recorder2 := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}}
|
||||
for range 60 {
|
||||
balancer.ServeHTTP(recorder2, httptest.NewRequest(http.MethodGet, "/", nil))
|
||||
}
|
||||
|
||||
// server2 should get significantly more traffic (>75%)
|
||||
// Score for server1: (~10-15ms × 1) / 1 = 10-15 (as average climbs)
|
||||
// Score for server2: (5ms × 1) / 1 = 5
|
||||
// server2 should get significantly more traffic
|
||||
// With 10x performance difference, server2 should dominate.
|
||||
total2 := recorder2.save["server1"] + recorder2.save["server2"]
|
||||
assert.Equal(t, 60, total2)
|
||||
assert.Greater(t, recorder2.save["server2"], 45) // At least 75% (45/60)
|
||||
assert.Less(t, recorder2.save["server1"], 15) // At most 25% (15/60)
|
||||
assert.Greater(t, recorder2.save["server2"], 35) // At least ~60% (35/60)
|
||||
assert.Less(t, recorder2.save["server1"], 25) // At most ~40% (25/60)
|
||||
}
|
||||
|
||||
// TestMultipleServersWithSameScore tests WRR tie-breaking when multiple servers have identical scores.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user