diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md index 2eaa9861b..193f0c65d 100644 --- a/docs/content/reference/static-configuration/cli-ref.md +++ b/docs/content/reference/static-configuration/cli-ref.md @@ -768,6 +768,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. @@ -1167,6 +1179,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 474fdc9ef..849d058ab 100644 --- a/docs/content/reference/static-configuration/env-ref.md +++ b/docs/content/reference/static-configuration/env-ref.md @@ -768,6 +768,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. @@ -1167,6 +1179,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 8e96fd52e..13fd6b2bc 100644 --- a/docs/content/reference/static-configuration/file.toml +++ b/docs/content/reference/static-configuration/file.toml @@ -98,6 +98,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 +126,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 3c79d9089..4c4fdc109 100644 --- a/docs/content/reference/static-configuration/file.yaml +++ b/docs/content/reference/static-configuration/file.yaml @@ -109,6 +109,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 @@ -126,6 +133,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/provider/configuration.go b/pkg/provider/configuration.go index bbb393425..70dd14144 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 b3bbe2411..3a6e75bee 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 44ff3470d..8deb4f146 100644 --- a/pkg/provider/docker/pdocker.go +++ b/pkg/provider/docker/pdocker.go @@ -49,8 +49,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 97c8d519b..027c398fd 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 {