diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index b920e1793..000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,3 +0,0 @@ -# These are supported funding model platforms - -github: traefik diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 8d124b341..000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,77 +0,0 @@ - - -### Do you want to request a *feature* or report a *bug*? - - - -Bug - - - -### What did you do? - - - -### What did you expect to see? - - - -### What did you see instead? - - - -### Output of `traefik version`: (_What version of Traefik are you using?_) - - - -``` -(paste your output here) -``` - -### What is your environment & configuration (arguments, toml, provider, platform, ...)? - -```toml -# (paste your configuration here) -``` - - - - -### If applicable, please paste the log output in DEBUG level (`--log.level=DEBUG` switch) - -``` -(paste your output here) -``` diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml deleted file mode 100644 index ce21d35ee..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ /dev/null @@ -1,82 +0,0 @@ -name: Bug Report (Traefik) -description: Create a report to help us improve. -body: - - type: checkboxes - id: terms - attributes: - label: Welcome! - description: | - The issue tracker is for reporting bugs and feature requests only. - For end-user related support questions, please use the [Traefik community forum](https://community.traefik.io/). - - All new/updated issues are triaged regularly by the maintainers. - All issues closed by a bot are subsequently double-checked by the maintainers. - - DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS. - - options: - - label: Yes, I've searched similar issues on [GitHub](https://github.com/traefik/traefik/issues) and didn't find any. - required: true - - label: Yes, I've searched similar issues on the [Traefik community forum](https://community.traefik.io) and didn't find any. - required: true - - - type: textarea - attributes: - label: What did you do? - description: | - How to write a good bug report? - - - Respect the issue template as much as possible. - - The title should be short and descriptive. - - Explain the conditions which led you to report this issue: the context. - - The context should lead to something, an idea or a problem that you’re facing. - - Remain clear and concise. - - Format your messages to help the reader focus on what matters and understand the structure of your message, use [Markdown syntax](https://help.github.com/articles/github-flavored-markdown) - placeholder: What did you do? - validations: - required: true - - - type: textarea - attributes: - label: What did you see instead? - placeholder: What did you see instead? - validations: - required: true - - - type: textarea - attributes: - label: What version of Traefik are you using? - description: | - `latest` is not considered as a valid version. - - Output of `traefik version`. - - For the Traefik Docker image (`docker run [IMAGE] version`), example: - ```console - $ docker run traefik version - ``` - placeholder: Paste your output here. - validations: - required: true - - - type: textarea - attributes: - label: What is your environment & configuration? - description: arguments, toml, provider, platform, ... - placeholder: Add information here. - value: | - ```yaml - # (paste your configuration here) - ``` - - Add more configuration information here. - validations: - required: true - - - type: textarea - attributes: - label: If applicable, please paste the log output in DEBUG level - description: "`--log.level=DEBUG` switch." - placeholder: Paste your output here. - validations: - required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 80c8c86b5..000000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,8 +0,0 @@ -blank_issues_enabled: false -contact_links: - - name: Traefik Community Support - url: https://community.traefik.io/ - about: If you have a question, or are looking for advice, please post on our Discuss forum! The community loves to chime in to help. Happy Coding! - - name: Traefik Helm Chart Issues - url: https://github.com/traefik/traefik-helm-chart - about: Are you submitting an issue or feature enhancement for the Traefik helm chart? Please post in the traefik-helm-chart GitHub Issues. diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml deleted file mode 100644 index 5a092594d..000000000 --- a/.github/ISSUE_TEMPLATE/feature-request.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Feature Request (Traefik) -description: Suggest an idea for this project. -body: - - type: checkboxes - id: terms - attributes: - label: Welcome! - description: | - The issue tracker is for reporting bugs and feature requests only. For end-user related support questions, please refer to one of the following: - - the Traefik community forum: https://community.traefik.io/ - - DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS. - options: - - label: Yes, I've searched similar issues on [GitHub](https://github.com/traefik/traefik/issues) and didn't find any. - required: true - - label: Yes, I've searched similar issues on the [Traefik community forum](https://community.traefik.io) and didn't find any. - required: true - - - type: textarea - attributes: - label: What did you expect to see? - description: | - How to write a good issue? - - - Respect the issue template as much as possible. - - The title should be short and descriptive. - - Explain the conditions which led you to report this issue: the context. - - The context should lead to something, an idea or a problem that you’re facing. - - Remain clear and concise. - - Format your messages to help the reader focus on what matters and understand the structure of your message, use [Markdown syntax](https://help.github.com/articles/github-flavored-markdown) - placeholder: What did you expect to see? - validations: - required: true diff --git a/.github/PULL_REQUEST_TEMPLATE/mergeback.md b/.github/PULL_REQUEST_TEMPLATE/mergeback.md deleted file mode 100644 index dfc856011..000000000 --- a/.github/PULL_REQUEST_TEMPLATE/mergeback.md +++ /dev/null @@ -1,7 +0,0 @@ -### What does this PR do? - -Merge v{{.Version}} into master - -### Motivation - -Be sync. diff --git a/.github/PULL_REQUEST_TEMPLATE/release.md b/.github/PULL_REQUEST_TEMPLATE/release.md deleted file mode 100644 index 226a85a73..000000000 --- a/.github/PULL_REQUEST_TEMPLATE/release.md +++ /dev/null @@ -1,7 +0,0 @@ -### What does this PR do? - -Prepare release v{{.Version}}. - -### Motivation - -Create a new release. diff --git a/.github/workflows/sync-docker-images.yaml b/.github/workflows/sync-docker-images.yaml deleted file mode 100644 index 6f0b3c103..000000000 --- a/.github/workflows/sync-docker-images.yaml +++ /dev/null @@ -1,26 +0,0 @@ -name: Sync Docker Images - -on: - workflow_dispatch: - schedule: - - cron: "0 0 * * *" # Run every day - -jobs: - sync: - runs-on: ubuntu-latest - permissions: - packages: write - contents: read - if: github.repository == 'traefik/traefik' - - steps: - - uses: actions/checkout@v4 - - - uses: imjasonh/setup-crane@v0.4 - - - name: Sync - run: | - EXCLUDED_TAGS="1.7.9-alpine v1.0.0-beta.392 v1.0.0-beta.404 v1.0.0-beta.704 v1.0.0-rc1 v1.7.9-alpine" - EXCLUDED_REGEX=$(echo $EXCLUDED_TAGS | sed 's/ /|/g') - diff <(crane ls traefik) <(crane ls ghcr.io/traefik/traefik) | grep '^<' | awk '{print $2}' | while read -r tag; do [[ "$tag" =~ ^($EXCLUDED_REGEX)$ ]] || (echo "Processing image: traefik:$tag"; crane cp "traefik:$tag" "ghcr.io/traefik/traefik:$tag"); done - crane cp traefik:latest ghcr.io/traefik/traefik:latest diff --git a/Dockerfile b/Dockerfile index 0e08e72da..10ee5e2b5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,6 +5,7 @@ RUN apk add --no-cache --no-progress ca-certificates tzdata ARG TARGETPLATFORM COPY ./dist/$TARGETPLATFORM/traefik / +COPY ./traefik.yml /etc/traefik/traefik.yml EXPOSE 80 VOLUME ["/tmp"] diff --git a/README.md b/README.md index 711e19b8c..472057a3a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +# My modified version of Traefik +slightly modified version of traefik, will be rebased on the latest tag from time to time +this version includes callbacks to external services on configuration update and custom docker label mappings +feel free to check out last 2 commits, they are like 100sloc together

@@ -42,14 +46,14 @@ Pointing Traefik at your orchestrator should be the _only_ configuration step yo Imagine that you have deployed a bunch of microservices with the help of an orchestrator (like Swarm or Kubernetes) or a service registry (like etcd or consul). Now you want users to access these microservices, and you need a reverse proxy. -Traditional reverse-proxies require that you configure _each_ route that will connect paths and subdomains to _each_ microservice. -In an environment where you add, remove, kill, upgrade, or scale your services _many_ times a day, the task of keeping the routes up to date becomes tedious. +Traditional reverse-proxies require that you configure _each_ route that will connect paths and subdomains to _each_ microservice. +In an environment where you add, remove, kill, upgrade, or scale your services _many_ times a day, the task of keeping the routes up to date becomes tedious. **This is when Traefik can help you!** -Traefik listens to your service registry/orchestrator API and instantly generates the routes so your microservices are connected to the outside world -- without further intervention from your part. +Traefik listens to your service registry/orchestrator API and instantly generates the routes so your microservices are connected to the outside world -- without further intervention from your part. -**Run Traefik and let it do the work for you!** +**Run Traefik and let it do the work for you!** _(But if you'd rather configure some of your routes manually, Traefik supports that too!)_ ![Architecture](docs/content/assets/img/traefik-architecture.png) diff --git a/cmd/traefik/traefik.go b/cmd/traefik/traefik.go index 8c2cab62c..3f35b280f 100644 --- a/cmd/traefik/traefik.go +++ b/cmd/traefik/traefik.go @@ -49,6 +49,7 @@ import ( "github.com/traefik/traefik/v3/pkg/tcp" traefiktls "github.com/traefik/traefik/v3/pkg/tls" "github.com/traefik/traefik/v3/pkg/version" + "github.com/traefik/traefik/v3/pkg/updater" ) func main() { @@ -199,6 +200,8 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err tsProviders := initTailscaleProviders(staticConfiguration, providerAggregator) + updaterProvider := updater.New(staticConfiguration); + // Observability metricRegistries := registerMetricClients(staticConfiguration.Metrics) @@ -388,6 +391,9 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err } }) + // Updater + watcher.AddListener(updaterProvider.HandleConfigUpdate) + return server.NewServer(routinesPool, serverEntryPointsTCP, serverEntryPointsUDP, watcher, observabilityMgr), nil } diff --git a/docs/content/reference/install-configuration/configuration-options.md b/docs/content/reference/install-configuration/configuration-options.md index 8da658919..596c69283 100644 --- a/docs/content/reference/install-configuration/configuration-options.md +++ b/docs/content/reference/install-configuration/configuration-options.md @@ -137,6 +137,7 @@ THIS FILE MUST NOT BE EDITED BY HAND | experimental.plugins._name_.version | plugin's version. | | | global.checknewversion | Periodically check if a new version has been released. | true | | global.sendanonymoususage | Periodically send anonymous usage statistics. If the option is not specified, it will be disabled by default. | false | +| global.updatercallbacks | Callback urls for updater script (example: https://localhost:8080/callback) | | | hostresolver | Enable CNAME Flattening. | false | | hostresolver.cnameflattening | A flag to enable/disable CNAME flattening | false | | hostresolver.resolvconfig | resolv.conf used for DNS resolving | /etc/resolv.conf | @@ -272,6 +273,10 @@ THIS FILE MUST NOT BE EDITED BY HAND | providers.docker.endpoint | Docker server endpoint. Can be a TCP or a Unix socket endpoint. | unix:///var/run/docker.sock | | providers.docker.exposedbydefault | Expose containers by default. | true | | providers.docker.httpclienttimeout | Client timeout for HTTP connections. | 0 | +| providers.docker.labelmap | Label shorthands. | | +| providers.docker.labelmap[0].from | Shorthand label. | | +| providers.docker.labelmap[0].to | Full label with templates. | | +| providers.docker.labelmap[0].value | Optional override; used instead of user input if set. | | | providers.docker.network | Default Docker network used. | | | providers.docker.password | Password for Basic HTTP authentication. | | | providers.docker.tls.ca | TLS CA | | @@ -421,6 +426,10 @@ THIS FILE MUST NOT BE EDITED BY HAND | providers.swarm.endpoint | Docker server endpoint. Can be a TCP or a Unix socket endpoint. | unix:///var/run/docker.sock | | providers.swarm.exposedbydefault | Expose containers by default. | true | | providers.swarm.httpclienttimeout | Client timeout for HTTP connections. | 0 | +| providers.swarm.labelmap | Label shorthands. | | +| providers.swarm.labelmap[0].from | Shorthand label. | | +| providers.swarm.labelmap[0].to | Full label with templates. | | +| providers.swarm.labelmap[0].value | Optional override; used instead of user input if set. | | | providers.swarm.network | Default Docker network used. | | | providers.swarm.password | Password for Basic HTTP authentication. | | | providers.swarm.refreshseconds | Polling interval for swarm mode. | 15 | diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md index 81cfb6e07..b06e80721 100644 --- a/docs/content/reference/static-configuration/cli-ref.md +++ b/docs/content/reference/static-configuration/cli-ref.md @@ -390,6 +390,9 @@ Periodically check if a new version has been released. (Default: ```true```) `--global.sendanonymoususage`: Periodically send anonymous usage statistics. If the option is not specified, it will be disabled by default. (Default: ```false```) +`--global.updatercallbacks`: +Callback urls for updater script (example: https://localhost:8080/callback) + `--hostresolver`: Enable CNAME Flattening. (Default: ```false```) @@ -795,6 +798,18 @@ Expose containers by default. (Default: ```true```) `--providers.docker.httpclienttimeout`: Client timeout for HTTP connections. (Default: ```0```) +`--providers.docker.labelmap`: +Label shorthands. + +`--providers.docker.labelmap[n].from`: +Shorthand label. + +`--providers.docker.labelmap[n].to`: +Full label with templates. + +`--providers.docker.labelmap[n].value`: +Optional override; used instead of user input if set. + `--providers.docker.network`: Default Docker network used. @@ -1242,6 +1257,18 @@ Expose containers by default. (Default: ```true```) `--providers.swarm.httpclienttimeout`: Client timeout for HTTP connections. (Default: ```0```) +`--providers.swarm.labelmap`: +Label shorthands. + +`--providers.swarm.labelmap[n].from`: +Shorthand label. + +`--providers.swarm.labelmap[n].to`: +Full label with templates. + +`--providers.swarm.labelmap[n].value`: +Optional override; used instead of user input if set. + `--providers.swarm.network`: Default Docker network used. diff --git a/docs/content/reference/static-configuration/env-ref.md b/docs/content/reference/static-configuration/env-ref.md index eec38608e..65c009454 100644 --- a/docs/content/reference/static-configuration/env-ref.md +++ b/docs/content/reference/static-configuration/env-ref.md @@ -390,6 +390,9 @@ Periodically check if a new version has been released. (Default: ```true```) `TRAEFIK_GLOBAL_SENDANONYMOUSUSAGE`: Periodically send anonymous usage statistics. If the option is not specified, it will be disabled by default. (Default: ```false```) +`TRAEFIK_GLOBAL_UPDATERCALLBACKS`: +Callback urls for updater script (example: https://localhost:8080/callback) + `TRAEFIK_HOSTRESOLVER`: Enable CNAME Flattening. (Default: ```false```) @@ -795,6 +798,18 @@ Expose containers by default. (Default: ```true```) `TRAEFIK_PROVIDERS_DOCKER_HTTPCLIENTTIMEOUT`: Client timeout for HTTP connections. (Default: ```0```) +`TRAEFIK_PROVIDERS_DOCKER_LABELMAP`: +Label shorthands. + +`TRAEFIK_PROVIDERS_DOCKER_LABELMAP_n_FROM`: +Shorthand label. + +`TRAEFIK_PROVIDERS_DOCKER_LABELMAP_n_TO`: +Full label with templates. + +`TRAEFIK_PROVIDERS_DOCKER_LABELMAP_n_VALUE`: +Optional override; used instead of user input if set. + `TRAEFIK_PROVIDERS_DOCKER_NETWORK`: Default Docker network used. @@ -1242,6 +1257,18 @@ Expose containers by default. (Default: ```true```) `TRAEFIK_PROVIDERS_SWARM_HTTPCLIENTTIMEOUT`: Client timeout for HTTP connections. (Default: ```0```) +`TRAEFIK_PROVIDERS_SWARM_LABELMAP`: +Label shorthands. + +`TRAEFIK_PROVIDERS_SWARM_LABELMAP_n_FROM`: +Shorthand label. + +`TRAEFIK_PROVIDERS_SWARM_LABELMAP_n_TO`: +Full label with templates. + +`TRAEFIK_PROVIDERS_SWARM_LABELMAP_n_VALUE`: +Optional override; used instead of user input if set. + `TRAEFIK_PROVIDERS_SWARM_NETWORK`: Default Docker network used. diff --git a/docs/content/reference/static-configuration/file.toml b/docs/content/reference/static-configuration/file.toml index de4fc3601..7d22522e1 100644 --- a/docs/content/reference/static-configuration/file.toml +++ b/docs/content/reference/static-configuration/file.toml @@ -3,6 +3,7 @@ [global] checkNewVersion = true sendAnonymousUsage = true + updaterCallbacks = ["foobar", "foobar"] [serversTransport] insecureSkipVerify = true @@ -98,6 +99,16 @@ password = "foobar" endpoint = "foobar" httpClientTimeout = "42s" + + [[providers.docker.labelMap]] + from = "foobar" + to = "foobar" + value = "foobar" + + [[providers.docker.labelMap]] + from = "foobar" + to = "foobar" + value = "foobar" [providers.docker.tls] ca = "foobar" cert = "foobar" @@ -116,6 +127,16 @@ endpoint = "foobar" httpClientTimeout = "42s" refreshSeconds = "42s" + + [[providers.swarm.labelMap]] + from = "foobar" + to = "foobar" + value = "foobar" + + [[providers.swarm.labelMap]] + from = "foobar" + to = "foobar" + value = "foobar" [providers.swarm.tls] ca = "foobar" cert = "foobar" diff --git a/docs/content/reference/static-configuration/file.yaml b/docs/content/reference/static-configuration/file.yaml index f1612dde6..64aae401b 100644 --- a/docs/content/reference/static-configuration/file.yaml +++ b/docs/content/reference/static-configuration/file.yaml @@ -3,6 +3,9 @@ global: checkNewVersion: true sendAnonymousUsage: true + updaterCallbacks: + - foobar + - foobar serversTransport: insecureSkipVerify: true rootCAs: @@ -107,6 +110,13 @@ providers: useBindPortIP: true watch: true defaultRule: foobar + labelMap: + - from: foobar + to: foobar + value: foobar + - from: foobar + to: foobar + value: foobar username: foobar password: foobar endpoint: foobar @@ -124,6 +134,13 @@ providers: useBindPortIP: true watch: true defaultRule: foobar + labelMap: + - from: foobar + to: foobar + value: foobar + - from: foobar + to: foobar + value: foobar username: foobar password: foobar endpoint: foobar diff --git a/pkg/config/static/static_config.go b/pkg/config/static/static_config.go index fb15a0b18..e64118963 100644 --- a/pkg/config/static/static_config.go +++ b/pkg/config/static/static_config.go @@ -113,6 +113,7 @@ type CertificateResolver struct { type Global struct { CheckNewVersion bool `description:"Periodically check if a new version has been released." json:"checkNewVersion,omitempty" toml:"checkNewVersion,omitempty" yaml:"checkNewVersion,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` SendAnonymousUsage bool `description:"Periodically send anonymous usage statistics. If the option is not specified, it will be disabled by default." json:"sendAnonymousUsage,omitempty" toml:"sendAnonymousUsage,omitempty" yaml:"sendAnonymousUsage,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + UpdaterCallbacks []string `description:"Callback urls for updater script (example: https://localhost:8080/callback)" json:"updaterCallbacks,omitempty" toml:"updaterCallbacks,omitempty" yaml:"updaterCallbacks,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` } // ServersTransport options to configure communication between Traefik and the servers. diff --git a/pkg/provider/configuration.go b/pkg/provider/configuration.go index 2872ed7e3..be18de7ce 100644 --- a/pkg/provider/configuration.go +++ b/pkg/provider/configuration.go @@ -396,8 +396,8 @@ func AddStore(configuration *dynamic.TLSConfiguration, storeName string, store t return reflect.DeepEqual(configuration.Stores[storeName], store) } -// MakeDefaultRuleTemplate creates the default rule template. -func MakeDefaultRuleTemplate(defaultRule string, funcMap template.FuncMap) (*template.Template, error) { +// MakeAnyTemplate creates a template with any name +func MakeAnyTemplate(name string, value string, funcMap template.FuncMap) (*template.Template, error) { defaultFuncMap := sprig.TxtFuncMap() defaultFuncMap["normalize"] = Normalize @@ -405,7 +405,12 @@ func MakeDefaultRuleTemplate(defaultRule string, funcMap template.FuncMap) (*tem defaultFuncMap[k] = fn } - return template.New("defaultRule").Funcs(defaultFuncMap).Parse(defaultRule) + return template.New(name).Funcs(defaultFuncMap).Parse(value) +} + +// MakeDefaultRuleTemplate creates the default rule template. +func MakeDefaultRuleTemplate(defaultRule string, funcMap template.FuncMap) (*template.Template, error) { + return MakeAnyTemplate("defaultRule", defaultRule, funcMap) } // BuildTCPRouterConfiguration builds a router configuration. diff --git a/pkg/provider/docker/config.go b/pkg/provider/docker/config.go index 713586295..0b322715a 100644 --- a/pkg/provider/docker/config.go +++ b/pkg/provider/docker/config.go @@ -1,6 +1,7 @@ package docker import ( + "bytes" "context" "errors" "fmt" @@ -28,15 +29,50 @@ func NewDynConfBuilder(configuration Shared, apiClient client.APIClient, swarm b return &DynConfBuilder{Shared: configuration, apiClient: apiClient, swarm: swarm} } +func (p *DynConfBuilder) applyLabels(container *dockerData, model interface{}) { + for _, item := range p.LabelMap { + value, ok := container.Labels[item.From] + if !ok { // label doesn't exist + continue + } + + writer := &bytes.Buffer{} + if err := item.toTpl.Execute(writer, model); err != nil { + continue // should never happen? + } + + to := writer.String() + if item.Value != nil { + container.Labels[to] = *item.Value + } else { + container.Labels[to] = value + } + } +} + func (p *DynConfBuilder) build(ctx context.Context, containersInspected []dockerData) *dynamic.Configuration { configurations := make(map[string]*dynamic.Configuration) for _, container := range containersInspected { + serviceName := getServiceName(container) + + model := struct { + Name string + ContainerName string + Labels *map[string]string + }{ + Name: serviceName, + ContainerName: strings.TrimPrefix(container.Name, "/"), + Labels: &container.Labels, + } + containerName := getServiceName(container) + "-" + container.ID logger := log.Ctx(ctx).With().Str("container", containerName).Logger() ctxContainer := logger.WithContext(ctx) + p.applyLabels(&container, model) + if !p.keepContainer(ctxContainer, container) { continue } @@ -83,18 +119,6 @@ func (p *DynConfBuilder) build(ctx context.Context, containersInspected []docker continue } - serviceName := getServiceName(container) - - model := struct { - Name string - ContainerName string - Labels map[string]string - }{ - Name: serviceName, - ContainerName: strings.TrimPrefix(container.Name, "/"), - Labels: container.Labels, - } - provider.BuildRouterConfiguration(ctx, confFromLabel.HTTP, serviceName, p.defaultRuleTpl, model) configurations[containerName] = confFromLabel diff --git a/pkg/provider/docker/pdocker.go b/pkg/provider/docker/pdocker.go index 0720746a3..5e88be51b 100644 --- a/pkg/provider/docker/pdocker.go +++ b/pkg/provider/docker/pdocker.go @@ -48,8 +48,15 @@ func (p *Provider) Init() error { if err != nil { return fmt.Errorf("error while parsing default rule: %w", err) } - p.defaultRuleTpl = defaultRuleTpl + + for _, item := range p.LabelMap { + toTpl, err := provider.MakeAnyTemplate(item.From, item.To, nil) + if err != nil { + return fmt.Errorf("error while parsing label %v: %w", item.To, err) + } + item.toTpl = toTpl + } return nil } diff --git a/pkg/provider/docker/shared.go b/pkg/provider/docker/shared.go index 8d2ef4001..931c61905 100644 --- a/pkg/provider/docker/shared.go +++ b/pkg/provider/docker/shared.go @@ -33,9 +33,19 @@ type Shared struct { Watch bool `description:"Watch Docker events." json:"watch,omitempty" toml:"watch,omitempty" yaml:"watch,omitempty" export:"true"` DefaultRule string `description:"Default rule." json:"defaultRule,omitempty" toml:"defaultRule,omitempty" yaml:"defaultRule,omitempty"` + LabelMap []*LabelMapItem `description:"Label shorthands." json:"labelMap,omitempty" toml:"labelMap,omitempty" yaml:"labelMap,omitempty"` + defaultRuleTpl *template.Template } +type LabelMapItem struct { + From string `description:"Shorthand label." json:"from,omitempty" toml:"from,omitempty" yaml:"from,omitempty"` + To string `description:"Full label with templates." json:"to,omitempty" toml:"to,omitempty" yaml:"to,omitempty"` + Value *string `description:"Optional override; used instead of user input if set." json:"value,omitempty" toml:"value,omitempty" yaml:"value,omitempty"` + toTpl *template.Template +} + + func inspectContainers(ctx context.Context, dockerClient client.ContainerAPIClient, containerID string) dockerData { containerInspected, err := dockerClient.ContainerInspect(ctx, containerID) if err != nil { diff --git a/pkg/updater/provider.go b/pkg/updater/provider.go new file mode 100644 index 000000000..26d1fc469 --- /dev/null +++ b/pkg/updater/provider.go @@ -0,0 +1,49 @@ +package updater + +import ( + "bytes" + "encoding/json" + "net/http" + + "github.com/rs/zerolog/log" + "github.com/traefik/traefik/v3/pkg/config/dynamic" + "github.com/traefik/traefik/v3/pkg/config/static" + "github.com/traefik/traefik/v3/pkg/safe" +) + +type Updater struct { + callbackUrls []string +} + +func New(config *static.Configuration) *Updater { + updater := &Updater{ + callbackUrls: config.Global.UpdaterCallbacks, + } + + return updater +} + +func (u *Updater) HandleConfigUpdate(cfg dynamic.Configuration) { + body, err := json.Marshal(cfg) + + if err != nil { + // should never happen? + log.Error().Err(err).Msg("Error while marshalling dynamic configuration data to json") + return + } + + requestBody := bytes.NewBuffer(body) + + for _, url := range u.callbackUrls { + safe.Go(func() { + resp, err := http.Post(url, "application/json", requestBody) + + if err != nil { + log.Error().Err(err).Str("url", url).Msg("Error while sending configuration data to callback") + } else { + log.Debug().Str("url", url).Msg("Configuration data sent") + resp.Body.Close() + } + }) + } +} diff --git a/schema.json b/schema.json new file mode 100644 index 000000000..0f8823ad9 --- /dev/null +++ b/schema.json @@ -0,0 +1,1851 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/traefik-v3.json", + "$defs": { + "CertificateResolverTailscaleStruct": { + "additionalProperties": false, + "type": "object" + }, + "acmeConfiguration": { + "additionalProperties": false, + "properties": { + "caCertificates": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + }, + "caServer": { + "type": "string" + }, + "caServerName": { + "type": "string" + }, + "caSystemCertPool": { + "type": "boolean" + }, + "certificatesDuration": { + "type": "integer" + }, + "dnsChallenge": { + "$ref": "#/$defs/acmeDNSChallenge" + }, + "eab": { + "$ref": "#/$defs/acmeEAB" + }, + "email": { + "type": "string" + }, + "httpChallenge": { + "$ref": "#/$defs/acmeHTTPChallenge" + }, + "keyType": { + "type": "string" + }, + "preferredChain": { + "type": "string" + }, + "storage": { + "type": "string" + }, + "tlsChallenge": { + "$ref": "#/$defs/acmeTLSChallenge" + } + }, + "type": "object" + }, + "acmeDNSChallenge": { + "additionalProperties": false, + "properties": { + "delayBeforeCheck": { + "type": "string" + }, + "disablePropagationCheck": { + "type": "boolean" + }, + "propagation": { + "$ref": "#/$defs/acmePropagation" + }, + "provider": { + "type": "string" + }, + "resolvers": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + } + }, + "type": "object" + }, + "acmeEAB": { + "additionalProperties": false, + "properties": { + "hmacEncoded": { + "type": "string" + }, + "kid": { + "type": "string" + } + }, + "type": "object" + }, + "acmeHTTPChallenge": { + "additionalProperties": false, + "properties": { + "entryPoint": { + "type": "string" + } + }, + "type": "object" + }, + "acmePropagation": { + "additionalProperties": false, + "properties": { + "delayBeforeChecks": { + "type": "string" + }, + "disableANSChecks": { + "type": "boolean" + }, + "disableChecks": { + "type": "boolean" + }, + "requireAllRNS": { + "type": "boolean" + } + }, + "type": "object" + }, + "acmeTLSChallenge": { + "additionalProperties": false, + "type": "object" + }, + "consulProviderBuilder": { + "additionalProperties": false, + "properties": { + "endpoints": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + }, + "namespaces": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + }, + "rootKey": { + "type": "string" + }, + "tls": { + "$ref": "#/$defs/typesClientTLS" + }, + "token": { + "type": "string" + } + }, + "type": "object" + }, + "consulcatalogEndpointConfig": { + "additionalProperties": false, + "properties": { + "address": { + "type": "string" + }, + "datacenter": { + "type": "string" + }, + "endpointWaitTime": { + "type": "string" + }, + "httpAuth": { + "$ref": "#/$defs/consulcatalogEndpointHTTPAuthConfig" + }, + "scheme": { + "type": "string" + }, + "tls": { + "$ref": "#/$defs/typesClientTLS" + }, + "token": { + "type": "string" + } + }, + "type": "object" + }, + "consulcatalogEndpointHTTPAuthConfig": { + "additionalProperties": false, + "properties": { + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + }, + "type": "object" + }, + "consulcatalogProviderBuilder": { + "additionalProperties": false, + "properties": { + "cache": { + "type": "boolean" + }, + "connectAware": { + "type": "boolean" + }, + "connectByDefault": { + "type": "boolean" + }, + "constraints": { + "type": "string" + }, + "defaultRule": { + "type": "string" + }, + "endpoint": { + "$ref": "#/$defs/consulcatalogEndpointConfig" + }, + "exposedByDefault": { + "type": "boolean" + }, + "namespaces": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + }, + "prefix": { + "type": "string" + }, + "refreshInterval": { + "type": "string" + }, + "requireConsistent": { + "type": "boolean" + }, + "serviceName": { + "type": "string" + }, + "stale": { + "type": "boolean" + }, + "strictChecks": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + }, + "watch": { + "type": "boolean" + } + }, + "type": "object" + }, + "crdProvider": { + "additionalProperties": false, + "properties": { + "allowCrossNamespace": { + "type": "boolean" + }, + "allowEmptyServices": { + "type": "boolean" + }, + "allowExternalNameServices": { + "type": "boolean" + }, + "certAuthFilePath": { + "type": "string" + }, + "disableClusterScopeResources": { + "type": "boolean" + }, + "endpoint": { + "type": "string" + }, + "ingressClass": { + "type": "string" + }, + "labelSelector": { + "type": "string" + }, + "namespaces": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + }, + "nativeLBByDefault": { + "type": "boolean" + }, + "throttleDuration": { + "type": "string" + }, + "token": { + "type": "string" + } + }, + "type": "object" + }, + "dockerProvider": { + "additionalProperties": false, + "properties": { + "allowEmptyServices": { + "type": "boolean" + }, + "constraints": { + "type": "string" + }, + "defaultRule": { + "type": "string" + }, + "endpoint": { + "type": "string" + }, + "exposedByDefault": { + "type": "boolean" + }, + "httpClientTimeout": { + "type": "string" + }, + "network": { + "type": "string" + }, + "password": { + "type": "string" + }, + "tls": { + "$ref": "#/$defs/typesClientTLS" + }, + "useBindPortIP": { + "type": "boolean" + }, + "username": { + "type": "string" + }, + "watch": { + "type": "boolean" + }, + "labelMap": { + "type": "array", + "items": { + "type": "object", + "properties": { + "from": { + "type": "string", + "description": "Shorthand label." + }, + "to": { + "type": "string", + "description": "Full label with templates." + }, + "value": { + "type": "string", + "description": "Optional override; used instead of user input if set." + } + }, + "required": ["from", "to"], + "additionalProperties": false + } + } + }, + "type": "object" + }, + "dockerSwarmProvider": { + "additionalProperties": false, + "properties": { + "allowEmptyServices": { + "type": "boolean" + }, + "constraints": { + "type": "string" + }, + "defaultRule": { + "type": "string" + }, + "endpoint": { + "type": "string" + }, + "exposedByDefault": { + "type": "boolean" + }, + "httpClientTimeout": { + "type": "string" + }, + "network": { + "type": "string" + }, + "password": { + "type": "string" + }, + "refreshSeconds": { + "type": "string" + }, + "tls": { + "$ref": "#/$defs/typesClientTLS" + }, + "useBindPortIP": { + "type": "boolean" + }, + "username": { + "type": "string" + }, + "watch": { + "type": "boolean" + } + }, + "type": "object" + }, + "ecsProvider": { + "additionalProperties": false, + "properties": { + "accessKeyID": { + "type": "string" + }, + "autoDiscoverClusters": { + "type": "boolean" + }, + "clusters": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + }, + "constraints": { + "type": "string" + }, + "defaultRule": { + "type": "string" + }, + "ecsAnywhere": { + "type": "boolean" + }, + "exposedByDefault": { + "type": "boolean" + }, + "healthyTasksOnly": { + "type": "boolean" + }, + "refreshSeconds": { + "type": "integer" + }, + "region": { + "type": "string" + }, + "secretAccessKey": { + "type": "string" + } + }, + "type": "object" + }, + "etcdProvider": { + "additionalProperties": false, + "properties": { + "endpoints": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + }, + "password": { + "type": "string" + }, + "rootKey": { + "type": "string" + }, + "tls": { + "$ref": "#/$defs/typesClientTLS" + }, + "username": { + "type": "string" + } + }, + "type": "object" + }, + "fileProvider": { + "additionalProperties": false, + "properties": { + "debugLogGeneratedTemplate": { + "type": "boolean" + }, + "directory": { + "type": "string" + }, + "filename": { + "type": "string" + }, + "watch": { + "type": "boolean" + } + }, + "type": "object" + }, + "gatewayProvider": { + "additionalProperties": false, + "properties": { + "certAuthFilePath": { + "type": "string" + }, + "endpoint": { + "type": "string" + }, + "experimentalChannel": { + "type": "boolean" + }, + "labelSelector": { + "type": "string" + }, + "namespaces": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + }, + "nativeLBByDefault": { + "type": "boolean" + }, + "statusAddress": { + "$ref": "#/$defs/gatewayStatusAddress" + }, + "throttleDuration": { + "type": "string" + }, + "token": { + "type": "string" + } + }, + "type": "object" + }, + "gatewayServiceRef": { + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + } + }, + "type": "object" + }, + "gatewayStatusAddress": { + "additionalProperties": false, + "properties": { + "hostname": { + "type": "string" + }, + "ip": { + "type": "string" + }, + "service": { + "$ref": "#/$defs/gatewayServiceRef" + } + }, + "type": "object" + }, + "httpProvider": { + "additionalProperties": false, + "properties": { + "endpoint": { + "type": "string" + }, + "headers": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "pollInterval": { + "type": "string" + }, + "pollTimeout": { + "type": "string" + }, + "tls": { + "$ref": "#/$defs/typesClientTLS" + } + }, + "required": ["endpoint"], + "type": "object" + }, + "ingressEndpointIngress": { + "additionalProperties": false, + "properties": { + "hostname": { + "type": "string" + }, + "ip": { + "type": "string" + }, + "publishedService": { + "type": "string" + } + }, + "type": "object" + }, + "ingressProvider": { + "additionalProperties": false, + "properties": { + "allowEmptyServices": { + "type": "boolean" + }, + "allowExternalNameServices": { + "type": "boolean" + }, + "certAuthFilePath": { + "type": "string" + }, + "disableClusterScopeResources": { + "type": "boolean" + }, + "disableIngressClassLookup": { + "type": "boolean" + }, + "endpoint": { + "type": "string" + }, + "ingressClass": { + "type": "string" + }, + "ingressEndpoint": { + "$ref": "#/$defs/ingressEndpointIngress" + }, + "labelSelector": { + "type": "string" + }, + "namespaces": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + }, + "nativeLBByDefault": { + "type": "boolean" + }, + "throttleDuration": { + "type": "string" + }, + "token": { + "type": "string" + } + }, + "type": "object" + }, + "nomadEndpointConfig": { + "additionalProperties": false, + "properties": { + "address": { + "type": "string" + }, + "endpointWaitTime": { + "type": "string" + }, + "region": { + "type": "string" + }, + "tls": { + "$ref": "#/$defs/typesClientTLS" + }, + "token": { + "type": "string" + } + }, + "type": "object" + }, + "nomadProviderBuilder": { + "additionalProperties": false, + "properties": { + "allowEmptyServices": { + "type": "boolean" + }, + "constraints": { + "type": "string" + }, + "defaultRule": { + "type": "string" + }, + "endpoint": { + "$ref": "#/$defs/nomadEndpointConfig" + }, + "exposedByDefault": { + "type": "boolean" + }, + "namespaces": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + }, + "prefix": { + "type": "string" + }, + "refreshInterval": { + "type": "string" + }, + "stale": { + "type": "boolean" + }, + "throttleDuration": { + "type": "string" + }, + "watch": { + "type": "boolean" + } + }, + "type": "object" + }, + "pingHandler": { + "additionalProperties": false, + "properties": { + "entryPoint": { + "type": "string" + }, + "manualRouting": { + "type": "boolean" + }, + "terminatingStatusCode": { + "type": "integer" + } + }, + "type": "object" + }, + "pluginsDescriptor": { + "additionalProperties": false, + "properties": { + "moduleName": { + "type": "string" + }, + "settings": { + "$ref": "#/$defs/pluginsSettings" + }, + "version": { + "type": "string" + } + }, + "type": "object" + }, + "pluginsLocalDescriptor": { + "additionalProperties": false, + "properties": { + "moduleName": { + "type": "string" + }, + "settings": { + "$ref": "#/$defs/pluginsSettings" + } + }, + "type": "object" + }, + "pluginsSettings": { + "additionalProperties": false, + "properties": { + "envs": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + }, + "mounts": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + } + }, + "type": "object" + }, + "redisProvider": { + "additionalProperties": false, + "properties": { + "db": { + "type": "integer" + }, + "endpoints": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + }, + "password": { + "type": "string" + }, + "rootKey": { + "type": "string" + }, + "sentinel": { + "$ref": "#/$defs/redisSentinel" + }, + "tls": { + "$ref": "#/$defs/typesClientTLS" + }, + "username": { + "type": "string" + } + }, + "type": "object" + }, + "redisSentinel": { + "additionalProperties": false, + "properties": { + "latencyStrategy": { + "type": "boolean" + }, + "masterName": { + "type": "string" + }, + "password": { + "type": "string" + }, + "randomStrategy": { + "type": "boolean" + }, + "replicaStrategy": { + "type": "boolean" + }, + "useDisconnectedReplicas": { + "type": "boolean" + }, + "username": { + "type": "string" + } + }, + "type": "object" + }, + "restProvider": { + "additionalProperties": false, + "properties": { + "insecure": { + "type": "boolean" + } + }, + "type": "object" + }, + "staticAPI": { + "additionalProperties": false, + "properties": { + "basePath": { + "type": "string" + }, + "dashboard": { + "type": "boolean" + }, + "debug": { + "type": "boolean" + }, + "disableDashboardAd": { + "type": "boolean" + }, + "insecure": { + "type": "boolean" + } + }, + "type": "object" + }, + "staticCertificateResolver": { + "additionalProperties": false, + "properties": { + "acme": { + "$ref": "#/$defs/acmeConfiguration" + }, + "tailscale": { + "$ref": "#/$defs/CertificateResolverTailscaleStruct" + } + }, + "type": "object" + }, + "staticCore": { + "additionalProperties": false, + "properties": { + "defaultRuleSyntax": { + "type": "string" + } + }, + "type": "object" + }, + "staticEntryPoint": { + "additionalProperties": false, + "properties": { + "address": { + "type": "string" + }, + "allowACMEByPass": { + "type": "boolean" + }, + "asDefault": { + "type": "boolean" + }, + "forwardedHeaders": { + "$ref": "#/$defs/staticForwardedHeaders" + }, + "http": { + "$ref": "#/$defs/staticHTTPConfig" + }, + "http2": { + "$ref": "#/$defs/staticHTTP2Config" + }, + "http3": { + "$ref": "#/$defs/staticHTTP3Config" + }, + "observability": { + "$ref": "#/$defs/staticObservabilityConfig" + }, + "proxyProtocol": { + "$ref": "#/$defs/staticProxyProtocol" + }, + "reusePort": { + "type": "boolean" + }, + "transport": { + "$ref": "#/$defs/staticEntryPointsTransport" + }, + "udp": { + "$ref": "#/$defs/staticUDPConfig" + } + }, + "type": "object" + }, + "staticEntryPointsTransport": { + "additionalProperties": false, + "properties": { + "keepAliveMaxRequests": { + "type": "integer" + }, + "keepAliveMaxTime": { + "type": "string" + }, + "lifeCycle": { + "$ref": "#/$defs/staticLifeCycle" + }, + "respondingTimeouts": { + "$ref": "#/$defs/staticRespondingTimeouts" + } + }, + "type": "object" + }, + "staticExperimental": { + "additionalProperties": false, + "properties": { + "abortOnPluginFailure": { + "type": "boolean" + }, + "fastProxy": { + "$ref": "#/$defs/staticFastProxyConfig" + }, + "kubernetesGateway": { + "type": "boolean" + }, + "localPlugins": { + "additionalProperties": { + "$ref": "#/$defs/pluginsLocalDescriptor" + }, + "type": "object" + }, + "otlplogs": { + "type": "boolean" + }, + "plugins": { + "additionalProperties": { + "$ref": "#/$defs/pluginsDescriptor" + }, + "type": "object" + } + }, + "type": "object" + }, + "staticFastProxyConfig": { + "additionalProperties": false, + "properties": { + "debug": { + "type": "boolean" + } + }, + "type": "object" + }, + "staticForwardedHeaders": { + "additionalProperties": false, + "properties": { + "connection": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + }, + "insecure": { + "type": "boolean" + }, + "trustedIPs": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + } + }, + "type": "object" + }, + "staticForwardingTimeouts": { + "additionalProperties": false, + "properties": { + "dialTimeout": { + "type": "string" + }, + "idleConnTimeout": { + "type": "string" + }, + "responseHeaderTimeout": { + "type": "string" + } + }, + "type": "object" + }, + "staticGlobal": { + "additionalProperties": false, + "properties": { + "checkNewVersion": { + "type": "boolean" + }, + "sendAnonymousUsage": { + "type": "boolean" + }, + "updaterCallbacks": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "type": "object" + }, + "staticHTTP2Config": { + "additionalProperties": false, + "properties": { + "maxConcurrentStreams": { + "type": "integer" + } + }, + "type": "object" + }, + "staticHTTP3Config": { + "additionalProperties": false, + "properties": { + "advertisedPort": { + "type": "integer" + } + }, + "type": "object" + }, + "staticHTTPConfig": { + "additionalProperties": false, + "properties": { + "encodeQuerySemicolons": { + "type": "boolean" + }, + "maxHeaderBytes": { + "type": "integer" + }, + "middlewares": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + }, + "redirections": { + "$ref": "#/$defs/staticRedirections" + }, + "tls": { + "$ref": "#/$defs/staticTLSConfig" + } + }, + "type": "object" + }, + "staticLifeCycle": { + "additionalProperties": false, + "properties": { + "graceTimeOut": { + "type": "string" + }, + "requestAcceptGraceTimeout": { + "type": "string" + } + }, + "type": "object" + }, + "staticObservabilityConfig": { + "additionalProperties": false, + "properties": { + "accessLogs": { + "type": "boolean" + }, + "metrics": { + "type": "boolean" + }, + "tracing": { + "type": "boolean" + } + }, + "type": "object" + }, + "staticProviders": { + "additionalProperties": false, + "properties": { + "consul": { + "$ref": "#/$defs/consulProviderBuilder" + }, + "consulCatalog": { + "$ref": "#/$defs/consulcatalogProviderBuilder" + }, + "docker": { + "$ref": "#/$defs/dockerProvider" + }, + "ecs": { + "$ref": "#/$defs/ecsProvider" + }, + "etcd": { + "$ref": "#/$defs/etcdProvider" + }, + "file": { + "$ref": "#/$defs/fileProvider" + }, + "http": { + "$ref": "#/$defs/httpProvider" + }, + "kubernetesCRD": { + "$ref": "#/$defs/crdProvider" + }, + "kubernetesGateway": { + "$ref": "#/$defs/gatewayProvider" + }, + "kubernetesIngress": { + "$ref": "#/$defs/ingressProvider" + }, + "nomad": { + "$ref": "#/$defs/nomadProviderBuilder" + }, + "plugin": { + "additionalProperties": { + "additionalProperties": {}, + "type": "object" + }, + "type": "object" + }, + "providersThrottleDuration": { + "type": "string" + }, + "redis": { + "$ref": "#/$defs/redisProvider" + }, + "rest": { + "$ref": "#/$defs/restProvider" + }, + "swarm": { + "$ref": "#/$defs/dockerSwarmProvider" + }, + "zooKeeper": { + "$ref": "#/$defs/zkProvider" + } + }, + "type": "object" + }, + "staticProxyProtocol": { + "additionalProperties": false, + "properties": { + "insecure": { + "type": "boolean" + }, + "trustedIPs": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + } + }, + "type": "object" + }, + "staticRedirectEntryPoint": { + "additionalProperties": false, + "properties": { + "permanent": { + "type": "boolean" + }, + "priority": { + "type": "integer" + }, + "scheme": { + "type": "string" + }, + "to": { + "type": "string" + } + }, + "type": "object" + }, + "staticRedirections": { + "additionalProperties": false, + "properties": { + "entryPoint": { + "$ref": "#/$defs/staticRedirectEntryPoint" + } + }, + "type": "object" + }, + "staticRespondingTimeouts": { + "additionalProperties": false, + "properties": { + "idleTimeout": { + "type": "string" + }, + "readTimeout": { + "type": "string" + }, + "writeTimeout": { + "type": "string" + } + }, + "type": "object" + }, + "staticServersTransport": { + "additionalProperties": false, + "properties": { + "forwardingTimeouts": { + "$ref": "#/$defs/staticForwardingTimeouts" + }, + "insecureSkipVerify": { + "type": "boolean" + }, + "maxIdleConnsPerHost": { + "type": "integer" + }, + "rootCAs": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + }, + "spiffe": { + "$ref": "#/$defs/staticSpiffe" + } + }, + "type": "object" + }, + "staticSpiffe": { + "additionalProperties": false, + "properties": { + "ids": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + }, + "trustDomain": { + "type": "string" + } + }, + "type": "object" + }, + "staticSpiffeClientConfig": { + "additionalProperties": false, + "properties": { + "workloadAPIAddr": { + "type": "string" + } + }, + "type": "object" + }, + "staticTCPServersTransport": { + "additionalProperties": false, + "properties": { + "dialKeepAlive": { + "type": "string" + }, + "dialTimeout": { + "type": "string" + }, + "terminationDelay": { + "type": "string" + }, + "tls": { + "$ref": "#/$defs/staticTLSClientConfig" + } + }, + "type": "object" + }, + "staticTLSClientConfig": { + "additionalProperties": false, + "properties": { + "insecureSkipVerify": { + "type": "boolean" + }, + "rootCAs": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + }, + "spiffe": { + "$ref": "#/$defs/staticSpiffe" + } + }, + "type": "object" + }, + "staticTLSConfig": { + "additionalProperties": false, + "properties": { + "certResolver": { + "type": "string" + }, + "domains": { + "items": { + "$ref": "#/$defs/typesDomain" + }, + "type": ["array", "null"] + }, + "options": { + "type": "string" + } + }, + "type": "object" + }, + "staticTracing": { + "additionalProperties": false, + "properties": { + "addInternals": { + "type": "boolean" + }, + "capturedRequestHeaders": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + }, + "capturedResponseHeaders": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + }, + "globalAttributes": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "otlp": { + "$ref": "#/$defs/typesOTelTracing" + }, + "resourceAttributes": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "safeQueryParams": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + }, + "sampleRate": { + "type": "number" + }, + "serviceName": { + "type": "string" + } + }, + "type": "object" + }, + "staticUDPConfig": { + "additionalProperties": false, + "properties": { + "timeout": { + "type": "string" + } + }, + "type": "object" + }, + "typesAccessLog": { + "additionalProperties": false, + "properties": { + "addInternals": { + "type": "boolean" + }, + "bufferingSize": { + "type": "integer" + }, + "fields": { + "$ref": "#/$defs/typesAccessLogFields" + }, + "filePath": { + "type": "string" + }, + "filters": { + "$ref": "#/$defs/typesAccessLogFilters" + }, + "format": { + "type": "string" + }, + "otlp": { + "$ref": "#/$defs/typesOTelLog" + } + }, + "type": "object" + }, + "typesAccessLogFields": { + "additionalProperties": false, + "properties": { + "defaultMode": { + "type": "string" + }, + "headers": { + "$ref": "#/$defs/typesFieldHeaders" + }, + "names": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + }, + "type": "object" + }, + "typesAccessLogFilters": { + "additionalProperties": false, + "properties": { + "minDuration": { + "type": "string" + }, + "retryAttempts": { + "type": "boolean" + }, + "statusCodes": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + } + }, + "type": "object" + }, + "typesClientTLS": { + "additionalProperties": false, + "properties": { + "ca": { + "type": "string" + }, + "cert": { + "type": "string" + }, + "insecureSkipVerify": { + "type": "boolean" + }, + "key": { + "type": "string" + } + }, + "type": "object" + }, + "typesDatadog": { + "additionalProperties": false, + "properties": { + "addEntryPointsLabels": { + "type": "boolean" + }, + "addRoutersLabels": { + "type": "boolean" + }, + "addServicesLabels": { + "type": "boolean" + }, + "address": { + "type": "string" + }, + "prefix": { + "type": "string" + }, + "pushInterval": { + "type": "string" + } + }, + "type": "object" + }, + "typesDomain": { + "additionalProperties": false, + "properties": { + "main": { + "type": "string" + }, + "sans": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + } + }, + "type": "object" + }, + "typesFieldHeaders": { + "additionalProperties": false, + "properties": { + "defaultMode": { + "type": "string" + }, + "names": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + }, + "type": "object" + }, + "typesHostResolverConfig": { + "additionalProperties": false, + "properties": { + "cnameFlattening": { + "type": "boolean" + }, + "resolvConfig": { + "type": "string" + }, + "resolvDepth": { + "type": "integer" + } + }, + "type": "object" + }, + "typesInfluxDB2": { + "additionalProperties": false, + "properties": { + "addEntryPointsLabels": { + "type": "boolean" + }, + "addRoutersLabels": { + "type": "boolean" + }, + "addServicesLabels": { + "type": "boolean" + }, + "additionalLabels": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "address": { + "type": "string" + }, + "bucket": { + "type": "string" + }, + "org": { + "type": "string" + }, + "pushInterval": { + "type": "string" + }, + "token": { + "type": "string" + } + }, + "type": "object" + }, + "typesMetrics": { + "additionalProperties": false, + "properties": { + "addInternals": { + "type": "boolean" + }, + "datadog": { + "$ref": "#/$defs/typesDatadog" + }, + "influxDB2": { + "$ref": "#/$defs/typesInfluxDB2" + }, + "otlp": { + "$ref": "#/$defs/typesOTLP" + }, + "prometheus": { + "$ref": "#/$defs/typesPrometheus" + }, + "statsD": { + "$ref": "#/$defs/typesStatsd" + } + }, + "type": "object" + }, + "typesOTLP": { + "additionalProperties": false, + "properties": { + "addEntryPointsLabels": { + "type": "boolean" + }, + "addRoutersLabels": { + "type": "boolean" + }, + "addServicesLabels": { + "type": "boolean" + }, + "explicitBoundaries": { + "items": { + "type": "number" + }, + "type": ["array", "null"] + }, + "grpc": { + "$ref": "#/$defs/typesOTelGRPC" + }, + "http": { + "$ref": "#/$defs/typesOTelHTTP" + }, + "pushInterval": { + "type": "string" + }, + "serviceName": { + "type": "string" + } + }, + "type": "object" + }, + "typesOTelGRPC": { + "additionalProperties": false, + "properties": { + "endpoint": { + "type": "string" + }, + "headers": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "insecure": { + "type": "boolean" + }, + "tls": { + "$ref": "#/$defs/typesClientTLS" + } + }, + "type": "object" + }, + "typesOTelHTTP": { + "additionalProperties": false, + "properties": { + "endpoint": { + "type": "string" + }, + "headers": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "tls": { + "$ref": "#/$defs/typesClientTLS" + } + }, + "type": "object" + }, + "typesOTelLog": { + "additionalProperties": false, + "properties": { + "grpc": { + "$ref": "#/$defs/typesOTelGRPC" + }, + "http": { + "$ref": "#/$defs/typesOTelHTTP" + }, + "resourceAttributes": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "serviceName": { + "type": "string" + } + }, + "type": "object" + }, + "typesOTelTracing": { + "additionalProperties": false, + "properties": { + "grpc": { + "$ref": "#/$defs/typesOTelGRPC" + }, + "http": { + "$ref": "#/$defs/typesOTelHTTP" + } + }, + "type": "object" + }, + "typesPrometheus": { + "additionalProperties": false, + "properties": { + "addEntryPointsLabels": { + "type": "boolean" + }, + "addRoutersLabels": { + "type": "boolean" + }, + "addServicesLabels": { + "type": "boolean" + }, + "buckets": { + "items": { + "type": "number" + }, + "type": ["array", "null"] + }, + "entryPoint": { + "type": "string" + }, + "headerLabels": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "manualRouting": { + "type": "boolean" + } + }, + "type": "object" + }, + "typesStatsd": { + "additionalProperties": false, + "properties": { + "addEntryPointsLabels": { + "type": "boolean" + }, + "addRoutersLabels": { + "type": "boolean" + }, + "addServicesLabels": { + "type": "boolean" + }, + "address": { + "type": "string" + }, + "prefix": { + "type": "string" + }, + "pushInterval": { + "type": "string" + } + }, + "type": "object" + }, + "typesTraefikLog": { + "additionalProperties": false, + "properties": { + "compress": { + "type": "boolean" + }, + "filePath": { + "type": "string" + }, + "format": { + "type": "string" + }, + "level": { + "type": "string" + }, + "maxAge": { + "type": "integer" + }, + "maxBackups": { + "type": "integer" + }, + "maxSize": { + "type": "integer" + }, + "noColor": { + "type": "boolean" + }, + "otlp": { + "$ref": "#/$defs/typesOTelLog" + } + }, + "type": "object" + }, + "zkProvider": { + "additionalProperties": false, + "properties": { + "endpoints": { + "items": { + "type": "string" + }, + "type": ["array", "null"] + }, + "password": { + "type": "string" + }, + "rootKey": { + "type": "string" + }, + "username": { + "type": "string" + } + }, + "type": "object" + } + }, + "title": "Traefik v3 Static Configuration", + "properties": { + "accessLog": { + "$ref": "#/$defs/typesAccessLog" + }, + "api": { + "$ref": "#/$defs/staticAPI" + }, + "certificatesResolvers": { + "additionalProperties": { + "$ref": "#/$defs/staticCertificateResolver" + }, + "type": "object" + }, + "core": { + "$ref": "#/$defs/staticCore" + }, + "entryPoints": { + "additionalProperties": { + "$ref": "#/$defs/staticEntryPoint" + }, + "type": "object" + }, + "experimental": { + "$ref": "#/$defs/staticExperimental" + }, + "global": { + "$ref": "#/$defs/staticGlobal" + }, + "hostResolver": { + "$ref": "#/$defs/typesHostResolverConfig" + }, + "log": { + "$ref": "#/$defs/typesTraefikLog" + }, + "metrics": { + "$ref": "#/$defs/typesMetrics" + }, + "ping": { + "$ref": "#/$defs/pingHandler" + }, + "providers": { + "$ref": "#/$defs/staticProviders" + }, + "serversTransport": { + "$ref": "#/$defs/staticServersTransport" + }, + "spiffe": { + "$ref": "#/$defs/staticSpiffeClientConfig" + }, + "tcpServersTransport": { + "$ref": "#/$defs/staticTCPServersTransport" + }, + "tracing": { + "$ref": "#/$defs/staticTracing" + } + }, + "type": "object" +}