Merge current v3.6 into master

This commit is contained in:
mmatur
2026-01-16 11:26:25 +01:00
423 changed files with 10392 additions and 5265 deletions
+2 -11
View File
@@ -84,13 +84,7 @@ func (c *searchCriterion) filterMiddleware(mns []string) bool {
return true
}
for _, mn := range mns {
if c.MiddlewareName == mn {
return true
}
}
return false
return slices.Contains(mns, c.MiddlewareName)
}
func pagination(request *http.Request, maximum int) (pageInfo, error) {
@@ -109,10 +103,7 @@ func pagination(request *http.Request, maximum int) (pageInfo, error) {
return pageInfo{}, fmt.Errorf("invalid request: page: %d, per_page: %d", page, perPage)
}
endIndex := startIndex + perPage
if endIndex >= maximum {
endIndex = maximum
}
endIndex := min(startIndex+perPage, maximum)
nextPage := 1
if page*perPage < maximum {
+1 -1
View File
@@ -15,7 +15,7 @@ func init() {
expvar.Publish("Goroutines2", expvar.Func(goroutines))
}
func goroutines() interface{} {
func goroutines() any {
return runtime.NumGoroutine()
}
+3 -1
View File
@@ -30,11 +30,13 @@ func writeError(rw http.ResponseWriter, msg string, code int) {
type serviceInfoRepresentation struct {
*runtime.ServiceInfo
ServerStatus map[string]string `json:"serverStatus,omitempty"`
}
type tcpServiceInfoRepresentation struct {
*runtime.TCPServiceInfo
ServerStatus map[string]string `json:"serverStatus,omitempty"`
}
@@ -164,7 +166,7 @@ func getProviderName(id string) string {
return strings.SplitN(id, "@", 2)[1]
}
func extractType(element interface{}) string {
func extractType(element any) string {
v := reflect.ValueOf(element).Elem()
for i := range v.NumField() {
field := v.Field(i)
+1
View File
@@ -15,6 +15,7 @@ import (
type entryPointRepresentation struct {
*static.EntryPoint
Name string `json:"name,omitempty"`
}
+1 -1
View File
@@ -235,7 +235,7 @@ func TestHandler_EntryPoints(t *testing.T) {
require.NoError(t, err)
if *updateExpected {
var results interface{}
var results any
err := json.Unmarshal(contents, &results)
require.NoError(t, err)
+3
View File
@@ -16,6 +16,7 @@ import (
type routerRepresentation struct {
*runtime.RouterInfo
Name string `json:"name,omitempty"`
Provider string `json:"provider,omitempty"`
}
@@ -34,6 +35,7 @@ func newRouterRepresentation(name string, rt *runtime.RouterInfo) routerRepresen
type serviceRepresentation struct {
*runtime.ServiceInfo
Name string `json:"name,omitempty"`
Provider string `json:"provider,omitempty"`
Type string `json:"type,omitempty"`
@@ -52,6 +54,7 @@ func newServiceRepresentation(name string, si *runtime.ServiceInfo) serviceRepre
type middlewareRepresentation struct {
*runtime.MiddlewareInfo
Name string `json:"name,omitempty"`
Provider string `json:"provider,omitempty"`
Type string `json:"type,omitempty"`
+1 -1
View File
@@ -1028,7 +1028,7 @@ func TestHandler_HTTP(t *testing.T) {
require.NoError(t, err)
if *updateExpected {
var results interface{}
var results any
err := json.Unmarshal(contents, &results)
require.NoError(t, err)
+2 -2
View File
@@ -239,7 +239,7 @@ func TestHandler_Overview(t *testing.T) {
KubernetesCRD: &crd.Provider{},
Rest: &rest.Provider{},
Plugin: map[string]static.PluginConf{
"test": map[string]interface{}{},
"test": map[string]any{},
},
},
},
@@ -292,7 +292,7 @@ func TestHandler_Overview(t *testing.T) {
require.NoError(t, err)
if *updateExpected {
var results interface{}
var results any
err := json.Unmarshal(contents, &results)
require.NoError(t, err)
+3
View File
@@ -15,6 +15,7 @@ import (
type tcpRouterRepresentation struct {
*runtime.TCPRouterInfo
Name string `json:"name,omitempty"`
Provider string `json:"provider,omitempty"`
}
@@ -29,6 +30,7 @@ func newTCPRouterRepresentation(name string, rt *runtime.TCPRouterInfo) tcpRoute
type tcpServiceRepresentation struct {
*runtime.TCPServiceInfo
Name string `json:"name,omitempty"`
Provider string `json:"provider,omitempty"`
Type string `json:"type,omitempty"`
@@ -47,6 +49,7 @@ func newTCPServiceRepresentation(name string, si *runtime.TCPServiceInfo) tcpSer
type tcpMiddlewareRepresentation struct {
*runtime.TCPMiddlewareInfo
Name string `json:"name,omitempty"`
Provider string `json:"provider,omitempty"`
Type string `json:"type,omitempty"`
+1 -1
View File
@@ -964,7 +964,7 @@ func TestHandler_TCP(t *testing.T) {
require.NoError(t, err)
if *updateExpected {
var results interface{}
var results any
err := json.Unmarshal(contents, &results)
require.NoError(t, err)
+1 -1
View File
@@ -178,7 +178,7 @@ func TestHandler_GetMiddleware(t *testing.T) {
middlewareName string
conf runtime.Configuration
expectedStatus int
expected interface{}
expected any
}{
{
desc: "Middleware not found",
+2
View File
@@ -15,6 +15,7 @@ import (
type udpRouterRepresentation struct {
*runtime.UDPRouterInfo
Name string `json:"name,omitempty"`
Provider string `json:"provider,omitempty"`
}
@@ -29,6 +30,7 @@ func newUDPRouterRepresentation(name string, rt *runtime.UDPRouterInfo) udpRoute
type udpServiceRepresentation struct {
*runtime.UDPServiceInfo
Name string `json:"name,omitempty"`
Provider string `json:"provider,omitempty"`
Type string `json:"type,omitempty"`
+1 -1
View File
@@ -594,7 +594,7 @@ func TestHandler_UDP(t *testing.T) {
require.NoError(t, err)
if *updateExpected {
var results interface{}
var results any
err := json.Unmarshal(contents, &results)
require.NoError(t, err)
+34 -33
View File
@@ -82,7 +82,7 @@ func logDeprecations(arguments []string) (bool, error) {
if filePath != "" {
// We don't rely on the Parser file loader here to avoid issues with unknown fields.
// Parse file content into a generic map.
var fileConfig map[string]interface{}
var fileConfig map[string]any
if err := file.Decode(filePath, &fileConfig); err != nil {
return false, fmt.Errorf("decoding configuration file %s: %w", filePath, err)
}
@@ -106,7 +106,7 @@ func logDeprecations(arguments []string) (bool, error) {
if len(vars) > 0 {
// We don't rely on the Parser env loader here to avoid issues with unknown fields.
// Decode environment variables to a generic map.
var envConfig map[string]interface{}
var envConfig map[string]any
if err := env.Decode(vars, env.DefaultNamePrefix, &envConfig); err != nil {
return false, fmt.Errorf("decoding environment variables: %w", err)
}
@@ -130,9 +130,9 @@ func logDeprecations(arguments []string) (bool, error) {
// flattenToLabels recursively flattens a nested map into label key-value pairs.
// Example: {"experimental": {"http3": true}} -> {"traefik.experimental.http3": "true"}.
func flattenToLabels(config interface{}, currKey string, labels map[string]string) {
func flattenToLabels(config any, currKey string, labels map[string]string) {
switch v := config.(type) {
case map[string]interface{}:
case map[string]any:
for key, value := range v {
newKey := key
if currKey != "" {
@@ -140,7 +140,7 @@ func flattenToLabels(config interface{}, currKey string, labels map[string]strin
}
flattenToLabels(value, newKey, labels)
}
case []interface{}:
case []any:
for i, item := range v {
newKey := currKey + "[" + strconv.Itoa(i) + "]"
flattenToLabels(item, newKey, labels)
@@ -168,7 +168,7 @@ func parseDeprecatedConfig(labels map[string]string) (*configuration, error) {
// Filter unknown nodes and check for deprecated options.
config := &configuration{}
filterUnknownNodes(reflect.TypeOf(config), node)
filterUnknownNodes(reflect.TypeFor[*configuration](), node)
// If no config remains we can return without error, to allow other loaders to proceed.
if node == nil || len(node.Children) == 0 {
@@ -271,7 +271,7 @@ func (c *configuration) deprecationNotice(logger zerolog.Logger) bool {
if c.Pilot != nil {
incompatible = true
logger.Error().Msg("Pilot configuration has been removed in v3, please remove all Pilot-related install configuration for Traefik to start." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#pilot")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v2-to-v3-details/#pilot")
}
incompatibleCore := c.Core.deprecationNotice(logger)
@@ -289,7 +289,7 @@ func (c *core) deprecationNotice(logger zerolog.Logger) bool {
if c != nil && c.DefaultRuleSyntax != "" {
logger.Error().Msg("`Core.DefaultRuleSyntax` option has been deprecated in v3.4, and will be removed in the next major version." +
" Please consider migrating all router rules to v3 syntax." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v3/#rule-syntax")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v2-to-v3-details/#router-rule-matchers")
}
return false
@@ -319,13 +319,14 @@ func (p *providers) deprecationNotice(logger zerolog.Logger) bool {
if p.Marathon != nil {
incompatible = true
logger.Error().Msg("Marathon provider has been removed in v3, please remove all Marathon-related install configuration for Traefik to start." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#marathon-provider")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v2-to-v3-details/#marathon-provider" +
"")
}
if p.Rancher != nil {
incompatible = true
logger.Error().Msg("Rancher provider has been removed in v3, please remove all Rancher-related install configuration for Traefik to start." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#rancher-v1-provider")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v2-to-v3-details/#rancher-v1-provider")
}
dockerIncompatible := p.Docker.deprecationNotice(logger)
@@ -367,14 +368,14 @@ func (d *docker) deprecationNotice(logger zerolog.Logger) bool {
if d.SwarmMode != nil {
incompatible = true
logger.Error().Msg("Docker provider `swarmMode` option has been removed in v3, please use the Swarm Provider instead." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#docker-docker-swarm")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v2-to-v3-details/#swarmmode")
}
if d.TLS != nil && d.TLS.CAOptional != nil {
incompatible = true
logger.Error().Msg("Docker provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." +
" Please remove all occurrences from the install configuration for Traefik to start." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tlscaoptional")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v2-to-v3-details/#tlscaoptional")
}
return incompatible
@@ -415,7 +416,7 @@ func (e *etcd) deprecationNotice(logger zerolog.Logger) bool {
incompatible = true
logger.Error().Msg("ETCD provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." +
" Please remove all occurrences from the install configuration for Traefik to start." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tlscaoptional_3")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v2-to-v3-details/#tlscaoptional_3")
}
return incompatible
@@ -436,7 +437,7 @@ func (r *redis) deprecationNotice(logger zerolog.Logger) bool {
incompatible = true
logger.Error().Msg("Redis provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." +
" Please remove all occurrences from the install configuration for Traefik to start." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tlscaoptional_4")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v2-to-v3-details/#tlscaoptional_4")
}
return incompatible
@@ -457,14 +458,14 @@ func (c *consul) deprecationNotice(logger zerolog.Logger) bool {
if c.Namespace != nil {
incompatible = true
logger.Error().Msg("Consul provider `namespace` option has been removed, please use the `namespaces` option instead." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#consul-provider")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v2-to-v3-details/#namespace")
}
if c.TLS != nil && c.TLS.CAOptional != nil {
incompatible = true
logger.Error().Msg("Consul provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." +
" Please remove all occurrences from the install configuration for Traefik to start." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tlscaoptional_1")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v2-to-v3-details/#tlscaoptional_1")
}
return incompatible
@@ -489,14 +490,14 @@ func (c *consulCatalog) deprecationNotice(logger zerolog.Logger) bool {
if c.Namespace != nil {
incompatible = true
logger.Error().Msg("ConsulCatalog provider `namespace` option has been removed, please use the `namespaces` option instead." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#consulcatalog-provider")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v2-to-v3-details/#namespace_1")
}
if c.Endpoint != nil && c.Endpoint.TLS != nil && c.Endpoint.TLS.CAOptional != nil {
incompatible = true
logger.Error().Msg("ConsulCatalog provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." +
" Please remove all occurrences from the install configuration for Traefik to start." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#endpointtlscaoptional")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v2-to-v3-details/#endpointtlscaoptional")
}
return incompatible
@@ -517,14 +518,14 @@ func (n *nomad) deprecationNotice(logger zerolog.Logger) bool {
if n.Namespace != nil {
incompatible = true
logger.Error().Msg("Nomad provider `namespace` option has been removed, please use the `namespaces` option instead." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#nomad-provider")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v2-to-v3-details/#namespace_2")
}
if n.Endpoint != nil && n.Endpoint.TLS != nil && n.Endpoint.TLS.CAOptional != nil {
incompatible = true
logger.Error().Msg("Nomad provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." +
" Please remove all occurrences from the install configuration for Traefik to start." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#endpointtlscaoptional_1")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v2-to-v3-details/#endpointtlscaoptional_1")
}
return incompatible
@@ -545,7 +546,7 @@ func (h *http) deprecationNotice(logger zerolog.Logger) bool {
incompatible = true
logger.Error().Msg("HTTP provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." +
" Please remove all occurrences from the install configuration for Traefik to start." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tlscaoptional_2")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v2-to-v3-details/#tlscaoptional_2")
}
return incompatible
@@ -563,7 +564,7 @@ func (i *ingress) deprecationNotice(logger zerolog.Logger) {
if i.DisableIngressClassLookup != nil {
logger.Error().Msg("Kubernetes Ingress provider `disableIngressClassLookup` option has been deprecated in v3.1, and will be removed in the next major version." +
" Please use the `disableClusterScopeResources` option instead." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v3/#ingressclasslookup")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v3/#ingressclass-lookup")
}
}
@@ -581,7 +582,7 @@ func (e *experimental) deprecationNotice(logger zerolog.Logger) bool {
if e.HTTP3 != nil {
logger.Error().Msg("HTTP3 is not an experimental feature in v3 and the associated enablement has been removed." +
" Please remove its usage from the install configuration for Traefik to start." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3-details/#http3")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v2-to-v3-details/#http3")
return true
}
@@ -589,13 +590,13 @@ func (e *experimental) deprecationNotice(logger zerolog.Logger) bool {
if e.KubernetesGateway != nil {
logger.Error().Msg("KubernetesGateway provider is not an experimental feature starting with v3.1." +
" Please remove its usage from the install configuration." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v3/#gateway-api-kubernetesgateway-provider")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v3/#gateway-api-kubernetesgateway-provider")
}
if e.KubernetesIngressNGINX != nil {
logger.Error().Msg("KubernetesIngressNGINX provider is not an experimental feature starting with v3.6.2." +
" Please remove its usage from the install configuration." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v3/#ingress-nginx-provider")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v3/#ingress-nginx-provider")
}
return false
@@ -622,7 +623,7 @@ func (t *tracing) deprecationNotice(logger zerolog.Logger) bool {
if t.SpanNameLimit != nil {
incompatible = true
logger.Error().Msg("SpanNameLimit option for Tracing has been removed in v3, as Span names are now of a fixed length." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tracing")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v2-to-v3-details/#tracing")
}
if t.GlobalAttributes != nil {
@@ -630,49 +631,49 @@ func (t *tracing) deprecationNotice(logger zerolog.Logger) bool {
logger.Error().Msg("`tracing.globalAttributes` option has been deprecated in v3.3, and will be removed in the next major version." +
" Please use the `tracing.resourceAttributes` option instead." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v3/#tracing-global-attributes")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v3/#tracing-global-attributes")
}
if t.Jaeger != nil {
incompatible = true
logger.Error().Msg("Jaeger Tracing backend has been removed in v3, please remove all Jaeger-related Tracing install configuration for Traefik to start." +
" In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tracing")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v2-to-v3-details/#tracing")
}
if t.Zipkin != nil {
incompatible = true
logger.Error().Msg("Zipkin Tracing backend has been removed in v3, please remove all Zipkin-related Tracing install configuration for Traefik to start." +
" In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tracing")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v2-to-v3-details/#tracing")
}
if t.Datadog != nil {
incompatible = true
logger.Error().Msg("Datadog Tracing backend has been removed in v3, please remove all Datadog-related Tracing install configuration for Traefik to start." +
" In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tracing")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v2-to-v3-details/#tracing")
}
if t.Instana != nil {
incompatible = true
logger.Error().Msg("Instana Tracing backend has been removed in v3, please remove all Instana-related Tracing install configuration for Traefik to start." +
" In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tracing")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v2-to-v3-details/#tracing")
}
if t.Haystack != nil {
incompatible = true
logger.Error().Msg("Haystack Tracing backend has been removed in v3, please remove all Haystack-related Tracing install configuration for Traefik to start." +
" In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tracing")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v2-to-v3-details/#tracing")
}
if t.Elastic != nil {
incompatible = true
logger.Error().Msg("Elastic Tracing backend has been removed in v3, please remove all Elastic-related Tracing install configuration for Traefik to start." +
" In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." +
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tracing")
" For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migrate/v2-to-v3-details/#tracing")
}
return incompatible
+1 -1
View File
@@ -62,7 +62,7 @@ func (f *FileLoader) Load(args []string, cmd *cli.Command) (bool, error) {
// loadConfigFiles tries to decode the given configuration file and all default locations for the configuration file.
// It stops as soon as decoding one of them is successful.
func loadConfigFiles(configFile string, element interface{}) (string, error) {
func loadConfigFiles(configFile string, element any) (string, error) {
finder := cli.Finder{
BasePaths: []string{"/etc/traefik/traefik", "$XDG_CONFIG_HOME/traefik", "$HOME/.config/traefik", "./traefik"},
Extensions: []string{"toml", "yaml", "yml"},
+2 -2
View File
@@ -18,7 +18,7 @@ const (
)
// Hydrate hydrates a configuration.
func Hydrate(element interface{}) error {
func Hydrate(element any) error {
field := reflect.ValueOf(element)
return fill(field)
}
@@ -81,7 +81,7 @@ func fill(field reflect.Value) error {
return nil
}
func setTyped(field reflect.Value, i interface{}) {
func setTyped(field reflect.Value, i any) {
baseValue := reflect.ValueOf(i)
if field.Kind().String() == field.Type().String() {
field.Set(baseValue)
+2 -2
View File
@@ -21,7 +21,7 @@ func TestDeepCopy(t *testing.T) {
cfgDeepCopy := cfg.DeepCopy()
assert.NotEqual(t, reflect.ValueOf(cfgDeepCopy), reflect.ValueOf(cfg))
assert.Equal(t, reflect.TypeOf(cfgDeepCopy), reflect.TypeOf(cfg))
assert.Equal(t, reflect.TypeOf(cfgDeepCopy), reflect.TypeOf(cfg)) //nolint:modernize // Comparing runtime types of two values.
assert.Equal(t, cfgDeepCopy, cfg)
// Update cfg
@@ -32,6 +32,6 @@ func TestDeepCopy(t *testing.T) {
assert.Equal(t, cfgCopy, cfg)
assert.NotEqual(t, reflect.ValueOf(cfgDeepCopy), reflect.ValueOf(cfg))
assert.Equal(t, reflect.TypeOf(cfgDeepCopy), reflect.TypeOf(cfg))
assert.Equal(t, reflect.TypeOf(cfgDeepCopy), reflect.TypeOf(cfg)) //nolint:modernize // Comparing runtime types of two values.
assert.NotEqual(t, cfgDeepCopy, cfg)
}
+6 -6
View File
@@ -72,12 +72,12 @@ type Router struct {
Rule string `json:"rule,omitempty" toml:"rule,omitempty" yaml:"rule,omitempty"`
ParentRefs []string `json:"parentRefs,omitempty" toml:"parentRefs,omitempty" yaml:"parentRefs,omitempty" label:"-" export:"true"`
// Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax.
RuleSyntax string `json:"ruleSyntax,omitempty" toml:"ruleSyntax,omitempty" yaml:"ruleSyntax,omitempty" export:"true"`
Priority int `json:"priority,omitempty" toml:"priority,omitempty,omitzero" yaml:"priority,omitempty" export:"true"`
TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
Observability *RouterObservabilityConfig `json:"observability,omitempty" toml:"observability,omitempty" yaml:"observability,omitempty" export:"true"`
DefaultRule bool `json:"-" toml:"-" yaml:"-" label:"-" file:"-"`
DeniedEncodedPathCharacters RouterDeniedEncodedPathCharacters `json:"-" toml:"-" yaml:"-" label:"-" file:"-"`
RuleSyntax string `json:"ruleSyntax,omitempty" toml:"ruleSyntax,omitempty" yaml:"ruleSyntax,omitempty" export:"true"`
Priority int `json:"priority,omitempty" toml:"priority,omitempty,omitzero" yaml:"priority,omitempty" export:"true"`
TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
Observability *RouterObservabilityConfig `json:"observability,omitempty" toml:"observability,omitempty" yaml:"observability,omitempty" export:"true"`
DefaultRule bool `json:"-" toml:"-" yaml:"-" label:"-" file:"-"`
DeniedEncodedPathCharacters *RouterDeniedEncodedPathCharacters `json:"-" toml:"-" yaml:"-" label:"-" file:"-" kv:"-"`
}
// +k8s:deepcopy-gen=true
+2
View File
@@ -69,6 +69,7 @@ type GrpcWeb struct {
type ContentType struct {
// AutoDetect specifies whether to let the `Content-Type` header, if it has not been set by the backend,
// be automatically set to a value derived from the contents of the response.
//
// Deprecated: AutoDetect option is deprecated, Content-Type middleware is only meant to be used to enable the content-type detection, please remove any usage of this option.
AutoDetect *bool `json:"autoDetect,omitempty" toml:"autoDetect,omitempty" yaml:"autoDetect,omitempty" export:"true"`
}
@@ -481,6 +482,7 @@ func (s *IPStrategy) Get() (ip.Strategy, error) {
// IPWhiteList holds the IP whitelist middleware configuration.
// This middleware limits allowed requests based on the client IP.
// More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/ipwhitelist/
//
// Deprecated: please use IPAllowList instead.
type IPWhiteList struct {
// SourceRange defines the set of allowed IPs (or ranges of allowed IPs by using CIDR notation). Required.
+3 -3
View File
@@ -44,11 +44,11 @@ func TestPluginConf_DeepCopy_mapOfStruct(t *testing.T) {
}
func TestPluginConf_DeepCopy_map(t *testing.T) {
m := map[string]interface{}{
m := map[string]any{
"name": "bar",
}
p := PluginConf{
"config": map[string]interface{}{
"config": map[string]any{
"foo": m,
},
}
@@ -64,7 +64,7 @@ func TestPluginConf_DeepCopy_map(t *testing.T) {
func TestPluginConf_DeepCopy_panic(t *testing.T) {
p := &PluginConf{
"config": map[string]interface{}{
"config": map[string]any{
"foo": &Foo{Name: "gigi"},
},
}
+2
View File
@@ -88,6 +88,7 @@ type TCPServersLoadBalancer struct {
Servers []TCPServer `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server" export:"true"`
ServersTransport string `json:"serversTransport,omitempty" toml:"serversTransport,omitempty" yaml:"serversTransport,omitempty" export:"true"`
// ProxyProtocol holds the PROXY Protocol configuration.
//
// Deprecated: use ServersTransport to configure ProxyProtocol instead.
ProxyProtocol *ProxyProtocol `json:"proxyProtocol,omitempty" toml:"proxyProtocol,omitempty" yaml:"proxyProtocol,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
// TerminationDelay, corresponds to the deadline that the proxy sets, after one
@@ -95,6 +96,7 @@ type TCPServersLoadBalancer struct {
// connection, to close the reading capability as well, hence fully terminating the
// connection. It is a duration in milliseconds, defaulting to 100. A negative value
// means an infinite deadline (i.e. the reading capability is never closed).
//
// Deprecated: use ServersTransport to configure the TerminationDelay instead.
TerminationDelay *int `json:"terminationDelay,omitempty" toml:"terminationDelay,omitempty" yaml:"terminationDelay,omitempty" export:"true"`
HealthCheck *TCPServerHealthCheck `json:"healthCheck,omitempty" toml:"healthCheck,omitempty" yaml:"healthCheck,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
+1
View File
@@ -26,6 +26,7 @@ type TCPInFlightConn struct {
// +k8s:deepcopy-gen=true
// TCPIPWhiteList holds the TCP IPWhiteList middleware configuration.
//
// Deprecated: please use IPAllowList instead.
type TCPIPWhiteList struct {
// SourceRange defines the allowed IPs (or ranges of allowed IPs by using CIDR notation).
+5 -1
View File
@@ -1389,7 +1389,11 @@ func (in *Router) DeepCopyInto(out *Router) {
*out = new(RouterObservabilityConfig)
(*in).DeepCopyInto(*out)
}
out.DeniedEncodedPathCharacters = in.DeniedEncodedPathCharacters
if in.DeniedEncodedPathCharacters != nil {
in, out := &in.DeniedEncodedPathCharacters, &out.DeniedEncodedPathCharacters
*out = new(RouterDeniedEncodedPathCharacters)
**out = **in
}
return
}
+2 -2
View File
@@ -13,7 +13,7 @@ import (
// KV pairs -> tree of untyped nodes
// untyped nodes -> nodes augmented with metadata such as kind (inferred from element)
// "typed" nodes -> typed element.
func Decode(pairs []*store.KVPair, element interface{}, rootName string) error {
func Decode(pairs []*store.KVPair, element any, rootName string) error {
if element == nil {
return nil
}
@@ -34,7 +34,7 @@ func Decode(pairs []*store.KVPair, element interface{}, rootName string) error {
return parser.Fill(element, node, parser.FillerOpts{AllowSliceAsStruct: false})
}
func getRootFieldNames(rootName string, element interface{}) []string {
func getRootFieldNames(rootName string, element any) []string {
if element == nil {
return nil
}
+1 -1
View File
@@ -31,6 +31,6 @@ func EncodeConfiguration(conf *dynamic.Configuration) (map[string]string, error)
// Decode converts the labels to an element.
// labels -> [ node -> node + metadata (type) ] -> element (node).
func Decode(labels map[string]string, element interface{}, filters ...string) error {
func Decode(labels map[string]string, element any, filters ...string) error {
return parser.Decode(labels, element, parser.DefaultRootName, filters...)
}
+11 -17
View File
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"maps"
"slices"
"sort"
"sync"
@@ -74,6 +75,7 @@ func unique(src []string) []string {
// RouterInfo holds information about a currently running HTTP router.
type RouterInfo struct {
*dynamic.Router // dynamic configuration
// Err contains all the errors that occurred during router's creation.
Err []string `json:"error,omitempty"`
// Status reports whether the router is disabled, in a warning state, or all good (enabled).
@@ -91,10 +93,8 @@ type RouterInfo struct {
// AddError adds err to r.Err, if it does not already exist.
// If critical is set, r is marked as disabled.
func (r *RouterInfo) AddError(err error, critical bool) {
for _, value := range r.Err {
if value == err.Error() {
return
}
if slices.Contains(r.Err, err.Error()) {
return
}
r.Err = append(r.Err, err.Error())
@@ -112,6 +112,7 @@ func (r *RouterInfo) AddError(err error, critical bool) {
// MiddlewareInfo holds information about a currently running middleware.
type MiddlewareInfo struct {
*dynamic.Middleware // dynamic configuration
// Err contains all the errors that occurred during service creation.
Err []string `json:"error,omitempty"`
Status string `json:"status,omitempty"`
@@ -121,10 +122,8 @@ type MiddlewareInfo struct {
// AddError adds err to s.Err, if it does not already exist.
// If critical is set, m is marked as disabled.
func (m *MiddlewareInfo) AddError(err error, critical bool) {
for _, value := range m.Err {
if value == err.Error() {
return
}
if slices.Contains(m.Err, err.Error()) {
return
}
m.Err = append(m.Err, err.Error())
@@ -142,6 +141,7 @@ func (m *MiddlewareInfo) AddError(err error, critical bool) {
// ServiceInfo holds information about a currently running service.
type ServiceInfo struct {
*dynamic.Service // dynamic configuration
// Err contains all the errors that occurred during service creation.
Err []string `json:"error,omitempty"`
// Status reports whether the service is disabled, in a warning state, or all good (enabled).
@@ -157,10 +157,8 @@ type ServiceInfo struct {
// AddError adds err to s.Err, if it does not already exist.
// If critical is set, s is marked as disabled.
func (s *ServiceInfo) AddError(err error, critical bool) {
for _, value := range s.Err {
if value == err.Error() {
return
}
if slices.Contains(s.Err, err.Error()) {
return
}
s.Err = append(s.Err, err.Error())
@@ -197,9 +195,5 @@ func (s *ServiceInfo) GetAllStatus() map[string]string {
return nil
}
allStatus := make(map[string]string, len(s.serverStatus))
for k, v := range s.serverStatus {
allStatus[k] = v
}
return allStatus
return maps.Clone(s.serverStatus)
}
+15 -19
View File
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"maps"
"slices"
"sync"
@@ -49,8 +50,9 @@ func (c *Configuration) GetTCPRoutersByEntryPoints(ctx context.Context, entryPoi
// TCPRouterInfo holds information about a currently running TCP router.
type TCPRouterInfo struct {
*dynamic.TCPRouter // dynamic configuration
Err []string `json:"error,omitempty"` // initialization error
*dynamic.TCPRouter // dynamic configuration
Err []string `json:"error,omitempty"` // initialization error
// Status reports whether the router is disabled, in a warning state, or all good (enabled).
// If not in "enabled" state, the reason for it should be in the list of Err.
// It is the caller's responsibility to set the initial status.
@@ -61,10 +63,8 @@ type TCPRouterInfo struct {
// AddError adds err to r.Err, if it does not already exist.
// If critical is set, r is marked as disabled.
func (r *TCPRouterInfo) AddError(err error, critical bool) {
for _, value := range r.Err {
if value == err.Error() {
return
}
if slices.Contains(r.Err, err.Error()) {
return
}
r.Err = append(r.Err, err.Error())
@@ -81,8 +81,9 @@ func (r *TCPRouterInfo) AddError(err error, critical bool) {
// TCPServiceInfo holds information about a currently running TCP service.
type TCPServiceInfo struct {
*dynamic.TCPService // dynamic configuration
Err []string `json:"error,omitempty"` // initialization error
*dynamic.TCPService // dynamic configuration
Err []string `json:"error,omitempty"` // initialization error
// Status reports whether the service is disabled, in a warning state, or all good (enabled).
// If not in "enabled" state, the reason for it should be in the list of Err.
// It is the caller's responsibility to set the initial status.
@@ -96,10 +97,8 @@ type TCPServiceInfo struct {
// AddError adds err to s.Err, if it does not already exist.
// If critical is set, s is marked as disabled.
func (s *TCPServiceInfo) AddError(err error, critical bool) {
for _, value := range s.Err {
if value == err.Error() {
return
}
if slices.Contains(s.Err, err.Error()) {
return
}
s.Err = append(s.Err, err.Error())
@@ -135,15 +134,14 @@ func (s *TCPServiceInfo) GetAllStatus() map[string]string {
}
allStatus := make(map[string]string, len(s.serverStatus))
for k, v := range s.serverStatus {
allStatus[k] = v
}
maps.Copy(allStatus, s.serverStatus)
return allStatus
}
// TCPMiddlewareInfo holds information about a currently running middleware.
type TCPMiddlewareInfo struct {
*dynamic.TCPMiddleware // dynamic configuration
// Err contains all the errors that occurred during service creation.
Err []string `json:"error,omitempty"`
Status string `json:"status,omitempty"`
@@ -153,10 +151,8 @@ type TCPMiddlewareInfo struct {
// AddError adds err to s.Err, if it does not already exist.
// If critical is set, m is marked as disabled.
func (m *TCPMiddlewareInfo) AddError(err error, critical bool) {
for _, value := range m.Err {
if value == err.Error() {
return
}
if slices.Contains(m.Err, err.Error()) {
return
}
m.Err = append(m.Err, err.Error())
+10 -12
View File
@@ -54,8 +54,9 @@ func (c *Configuration) GetUDPRoutersByEntryPoints(ctx context.Context, entryPoi
// UDPRouterInfo holds information about a currently running UDP router.
type UDPRouterInfo struct {
*dynamic.UDPRouter // dynamic configuration
Err []string `json:"error,omitempty"` // initialization error
*dynamic.UDPRouter // dynamic configuration
Err []string `json:"error,omitempty"` // initialization error
// Status reports whether the router is disabled, in a warning state, or all good (enabled).
// If not in "enabled" state, the reason for it should be in the list of Err.
// It is the caller's responsibility to set the initial status.
@@ -66,10 +67,8 @@ type UDPRouterInfo struct {
// AddError adds err to r.Err, if it does not already exist.
// If critical is set, r is marked as disabled.
func (r *UDPRouterInfo) AddError(err error, critical bool) {
for _, value := range r.Err {
if value == err.Error() {
return
}
if slices.Contains(r.Err, err.Error()) {
return
}
r.Err = append(r.Err, err.Error())
@@ -86,8 +85,9 @@ func (r *UDPRouterInfo) AddError(err error, critical bool) {
// UDPServiceInfo holds information about a currently running UDP service.
type UDPServiceInfo struct {
*dynamic.UDPService // dynamic configuration
Err []string `json:"error,omitempty"` // initialization error
*dynamic.UDPService // dynamic configuration
Err []string `json:"error,omitempty"` // initialization error
// Status reports whether the service is disabled, in a warning state, or all good (enabled).
// If not in "enabled" state, the reason for it should be in the list of Err.
// It is the caller's responsibility to set the initial status.
@@ -98,10 +98,8 @@ type UDPServiceInfo struct {
// AddError adds err to s.Err, if it does not already exist.
// If critical is set, s is marked as disabled.
func (s *UDPServiceInfo) AddError(err error, critical bool) {
for _, value := range s.Err {
if value == err.Error() {
return
}
if slices.Contains(s.Err, err.Error()) {
return
}
s.Err = append(s.Err, err.Error())
+10
View File
@@ -92,6 +92,16 @@ type EncodedCharacters struct {
AllowEncodedHash bool `description:"Defines whether requests with encoded hash characters in the path are allowed." json:"allowEncodedHash,omitempty" toml:"allowEncodedHash,omitempty" yaml:"allowEncodedHash,omitempty" export:"true"`
}
func (ec *EncodedCharacters) SetDefaults() {
ec.AllowEncodedSlash = true
ec.AllowEncodedBackSlash = true
ec.AllowEncodedNullCharacter = true
ec.AllowEncodedSemicolon = true
ec.AllowEncodedPercent = true
ec.AllowEncodedQuestionMark = true
ec.AllowEncodedHash = true
}
// HTTP2Config is the HTTP2 configuration of an entry point.
type HTTP2Config struct {
MaxConcurrentStreams int32 `description:"Specifies the number of concurrent streams per connection that each client is allowed to initiate." json:"maxConcurrentStreams,omitempty" toml:"maxConcurrentStreams,omitempty" yaml:"maxConcurrentStreams,omitempty" export:"true"`
+1 -1
View File
@@ -1,4 +1,4 @@
package static
// PluginConf holds the plugin configuration.
type PluginConf map[string]interface{}
type PluginConf map[string]any
+15 -15
View File
@@ -396,21 +396,6 @@ func (c *Configuration) SetEffectiveConfiguration() {
c.initACMEProvider()
}
func (c *Configuration) hasUserDefinedEntrypoint() bool {
return len(c.EntryPoints) != 0
}
func (c *Configuration) initACMEProvider() {
for _, resolver := range c.CertificatesResolvers {
if resolver.ACME != nil {
resolver.ACME.CAServer = getSafeACMECAServer(resolver.ACME.CAServer)
}
}
logger := logs.NoLevel(log.Logger, zerolog.DebugLevel).With().Str("lib", "lego").Logger()
legolog.Logger = logs.NewLogrusWrapper(logger)
}
// ValidateConfiguration validate that configuration is coherent.
func (c *Configuration) ValidateConfiguration() error {
for name, resolver := range c.CertificatesResolvers {
@@ -497,6 +482,21 @@ func (c *Configuration) ValidateConfiguration() error {
return nil
}
func (c *Configuration) hasUserDefinedEntrypoint() bool {
return len(c.EntryPoints) != 0
}
func (c *Configuration) initACMEProvider() {
for _, resolver := range c.CertificatesResolvers {
if resolver.ACME != nil {
resolver.ACME.CAServer = getSafeACMECAServer(resolver.ACME.CAServer)
}
}
logger := logs.NoLevel(log.Logger, zerolog.DebugLevel).With().Str("lib", "lego").Logger()
legolog.Logger = logs.NewLogrusWrapper(logger)
}
func getSafeACMECAServer(caServerSrc string) string {
if len(caServerSrc) == 0 {
return DefaultAcmeCAServer
+1 -1
View File
@@ -391,7 +391,7 @@ func (p *PassiveServiceHealthChecker) WrapHandler(ctx context.Context, next http
}
// We need to guarantee that only one goroutine (request) will update the status and create a timer for the target.
_, _, _ = p.timersGroup.Do(targetURL, func() (interface{}, error) {
_, _, _ = p.timersGroup.Do(targetURL, func() (any, error) {
// A timer is already running for this target;
// it means that the target is already considered unhealthy.
if _, ok := p.timers.Load(targetURL); ok {
+1
View File
@@ -165,6 +165,7 @@ type testLoadBalancer struct {
// RWMutex needed due to parallel test execution: Both the system-under-test
// and the test assertions reference the counters.
*sync.RWMutex
numRemovedServers int
numUpsertedServers int
}
+1
View File
@@ -17,6 +17,7 @@ const (
// If operation() takes more than MinJobInterval, Reset() is called in NextBackOff().
type BackOff struct {
*backoff.ExponentialBackOff
MinJobInterval time.Duration
}
@@ -12,7 +12,7 @@ import (
func TestConcatFieldHandler_ServeHTTP(t *testing.T) {
testCases := []struct {
desc string
existingValue interface{}
existingValue any
newValue string
expectedResult string
}{
+1 -1
View File
@@ -129,7 +129,7 @@ func init() {
}
// CoreLogData holds the fields computed from the request/response.
type CoreLogData map[string]interface{}
type CoreLogData map[string]any
// LogData is the data captured by the middleware so that it can be logged.
type LogData struct {
+51 -51
View File
@@ -74,20 +74,6 @@ type Handler struct {
wg sync.WaitGroup
}
// AliceConstructor returns an alice.Constructor that wraps the Handler (conditionally) in a middleware chain.
func (h *Handler) AliceConstructor() alice.Constructor {
return func(next http.Handler) (http.Handler, error) {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
if h == nil {
next.ServeHTTP(rw, req)
return
}
h.ServeHTTP(rw, req, next)
}), nil
}
}
// NewHandler creates a new Handler.
func NewHandler(ctx context.Context, config *otypes.AccessLog) (*Handler, error) {
var file io.WriteCloser = noopCloser{os.Stdout}
@@ -185,28 +171,18 @@ func NewHandler(ctx context.Context, config *otypes.AccessLog) (*Handler, error)
return logHandler, nil
}
func openAccessLogFile(filePath string) (*os.File, error) {
dir := filepath.Dir(filePath)
// AliceConstructor returns an alice.Constructor that wraps the Handler (conditionally) in a middleware chain.
func (h *Handler) AliceConstructor() alice.Constructor {
return func(next http.Handler) (http.Handler, error) {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
if h == nil {
next.ServeHTTP(rw, req)
return
}
if err := os.MkdirAll(dir, 0o755); err != nil {
return nil, fmt.Errorf("failed to create log path %s: %w", dir, err)
h.ServeHTTP(rw, req, next)
}), nil
}
file, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o664)
if err != nil {
return nil, fmt.Errorf("error opening file %s: %w", filePath, err)
}
return file, nil
}
// GetLogData gets the request context object that contains logging data.
// This creates data as the request passes through the middleware chain.
func GetLogData(req *http.Request) *LogData {
if ld, ok := req.Context().Value(DataTableKey).(*LogData); ok {
return ld
}
return nil
}
func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http.Handler) {
@@ -337,23 +313,6 @@ func (h *Handler) Rotate() error {
return nil
}
func silentSplitHostPort(value string) (host, port string) {
host, port, err := net.SplitHostPort(value)
if err != nil {
return value, "-"
}
return host, port
}
func usernameIfPresent(theURL *url.URL) string {
if theURL.User != nil {
if name := theURL.User.Username(); name != "" {
return name
}
}
return "-"
}
// Logging handler to log frontend name, backend name, and elapsed time.
func (h *Handler) logTheRoundTrip(ctx context.Context, logDataTable *LogData) {
core := logDataTable.Core
@@ -460,6 +419,47 @@ func (h *Handler) keepAccessLog(statusCode, retryAttempts int, duration time.Dur
return false
}
// GetLogData gets the request context object that contains logging data.
// This creates data as the request passes through the middleware chain.
func GetLogData(req *http.Request) *LogData {
if ld, ok := req.Context().Value(DataTableKey).(*LogData); ok {
return ld
}
return nil
}
func openAccessLogFile(filePath string) (*os.File, error) {
dir := filepath.Dir(filePath)
if err := os.MkdirAll(dir, 0o755); err != nil {
return nil, fmt.Errorf("failed to create log path %s: %w", dir, err)
}
file, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o664)
if err != nil {
return nil, fmt.Errorf("error opening file %s: %w", filePath, err)
}
return file, nil
}
func silentSplitHostPort(value string) (host, port string) {
host, port, err := net.SplitHostPort(value)
if err != nil {
return value, "-"
}
return host, port
}
func usernameIfPresent(theURL *url.URL) string {
if theURL.User != nil {
if name := theURL.User.Username(); name != "" {
return name
}
}
return "-"
}
var requestCounter uint64 // Request ID
func nextRequestCount() uint64 {
@@ -81,7 +81,7 @@ func (f *GenericCLFLogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
return b.Bytes(), err
}
func toLog(fields logrus.Fields, key, defaultValue string, quoted bool) interface{} {
func toLog(fields logrus.Fields, key, defaultValue string, quoted bool) any {
if v, ok := fields[key]; ok {
if v == nil {
return defaultValue
@@ -15,12 +15,12 @@ func TestCommonLogFormatter_Format(t *testing.T) {
testCases := []struct {
name string
data map[string]interface{}
data map[string]any
expectedLog string
}{
{
name: "DownstreamStatus & DownstreamContentSize are nil",
data: map[string]interface{}{
data: map[string]any{
StartUTC: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
Duration: 123 * time.Second,
ClientHost: "10.0.0.1",
@@ -41,7 +41,7 @@ func TestCommonLogFormatter_Format(t *testing.T) {
},
{
name: "all data",
data: map[string]interface{}{
data: map[string]any{
StartUTC: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
Duration: 123 * time.Second,
ClientHost: "10.0.0.1",
@@ -62,7 +62,7 @@ func TestCommonLogFormatter_Format(t *testing.T) {
},
{
name: "all data with local time",
data: map[string]interface{}{
data: map[string]any{
StartLocal: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
Duration: 123 * time.Second,
ClientHost: "10.0.0.1",
@@ -106,12 +106,12 @@ func TestGenericCLFLogFormatter_Format(t *testing.T) {
testCases := []struct {
name string
data map[string]interface{}
data map[string]any
expectedLog string
}{
{
name: "DownstreamStatus & DownstreamContentSize are nil",
data: map[string]interface{}{
data: map[string]any{
StartUTC: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
Duration: 123 * time.Second,
ClientHost: "10.0.0.1",
@@ -132,7 +132,7 @@ func TestGenericCLFLogFormatter_Format(t *testing.T) {
},
{
name: "all data",
data: map[string]interface{}{
data: map[string]any{
StartUTC: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
Duration: 123 * time.Second,
ClientHost: "10.0.0.1",
@@ -153,7 +153,7 @@ func TestGenericCLFLogFormatter_Format(t *testing.T) {
},
{
name: "all data with local time",
data: map[string]interface{}{
data: map[string]any{
StartLocal: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
Duration: 123 * time.Second,
ClientHost: "10.0.0.1",
@@ -199,7 +199,7 @@ func Test_toLog(t *testing.T) {
fieldName string
defaultValue string
quoted bool
expectedLog interface{}
expectedLog any
}{
{
desc: "Should return int 1",
+20 -20
View File
@@ -503,32 +503,32 @@ func TestLoggerGenericCLFWithBufferingSize(t *testing.T) {
assertValidGenericCLFLogData(t, expectedLog, logData)
}
func assertString(exp string) func(t *testing.T, actual interface{}) {
return func(t *testing.T, actual interface{}) {
func assertString(exp string) func(t *testing.T, actual any) {
return func(t *testing.T, actual any) {
t.Helper()
assert.Equal(t, exp, actual)
}
}
func assertNotEmpty() func(t *testing.T, actual interface{}) {
return func(t *testing.T, actual interface{}) {
func assertNotEmpty() func(t *testing.T, actual any) {
return func(t *testing.T, actual any) {
t.Helper()
assert.NotEmpty(t, actual)
}
}
func assertFloat64(exp float64) func(t *testing.T, actual interface{}) {
return func(t *testing.T, actual interface{}) {
func assertFloat64(exp float64) func(t *testing.T, actual any) {
return func(t *testing.T, actual any) {
t.Helper()
assert.InDelta(t, exp, actual, delta)
}
}
func assertFloat64NotZero() func(t *testing.T, actual interface{}) {
return func(t *testing.T, actual interface{}) {
func assertFloat64NotZero() func(t *testing.T, actual any) {
return func(t *testing.T, actual any) {
t.Helper()
assert.NotZero(t, actual)
@@ -541,7 +541,7 @@ func TestLoggerJSON(t *testing.T) {
config *otypes.AccessLog
tls bool
tracing bool
expected map[string]func(t *testing.T, value interface{})
expected map[string]func(t *testing.T, value any)
}{
{
desc: "default config without tracing",
@@ -549,7 +549,7 @@ func TestLoggerJSON(t *testing.T) {
FilePath: "",
Format: JSONFormat,
},
expected: map[string]func(t *testing.T, value interface{}){
expected: map[string]func(t *testing.T, value any){
RequestContentSize: assertFloat64(0),
RequestHost: assertString(testHostname),
RequestAddr: assertString(testHostname),
@@ -589,7 +589,7 @@ func TestLoggerJSON(t *testing.T) {
Format: JSONFormat,
},
tracing: true,
expected: map[string]func(t *testing.T, value interface{}){
expected: map[string]func(t *testing.T, value any){
RequestContentSize: assertFloat64(0),
RequestHost: assertString(testHostname),
RequestAddr: assertString(testHostname),
@@ -631,7 +631,7 @@ func TestLoggerJSON(t *testing.T) {
Format: JSONFormat,
},
tls: true,
expected: map[string]func(t *testing.T, value interface{}){
expected: map[string]func(t *testing.T, value any){
RequestContentSize: assertFloat64(0),
RequestHost: assertString(testHostname),
RequestAddr: assertString(testHostname),
@@ -676,7 +676,7 @@ func TestLoggerJSON(t *testing.T) {
DefaultMode: "drop",
},
},
expected: map[string]func(t *testing.T, value interface{}){
expected: map[string]func(t *testing.T, value any){
"level": assertString("info"),
"msg": assertString(""),
"time": assertNotEmpty(),
@@ -697,7 +697,7 @@ func TestLoggerJSON(t *testing.T) {
},
},
},
expected: map[string]func(t *testing.T, value interface{}){
expected: map[string]func(t *testing.T, value any){
"level": assertString("info"),
"msg": assertString(""),
"time": assertNotEmpty(),
@@ -715,7 +715,7 @@ func TestLoggerJSON(t *testing.T) {
},
},
},
expected: map[string]func(t *testing.T, value interface{}){
expected: map[string]func(t *testing.T, value any){
"level": assertString("info"),
"msg": assertString(""),
"time": assertNotEmpty(),
@@ -742,7 +742,7 @@ func TestLoggerJSON(t *testing.T) {
},
},
},
expected: map[string]func(t *testing.T, value interface{}){
expected: map[string]func(t *testing.T, value any){
RequestHost: assertString(testHostname),
"level": assertString("info"),
"msg": assertString(""),
@@ -768,7 +768,7 @@ func TestLoggerJSON(t *testing.T) {
},
},
},
expected: map[string]func(t *testing.T, value interface{}){
expected: map[string]func(t *testing.T, value any){
RequestHost: assertString(testHostname),
"level": assertString("info"),
"msg": assertString(""),
@@ -794,7 +794,7 @@ func TestLoggerJSON(t *testing.T) {
logData, err := os.ReadFile(logFilePath)
require.NoError(t, err)
jsonData := make(map[string]interface{})
jsonData := make(map[string]any)
err = json.Unmarshal(logData, &jsonData)
require.NoError(t, err)
@@ -808,7 +808,7 @@ func TestLoggerJSON(t *testing.T) {
}
func TestLogger_AbortedRequest(t *testing.T) {
expected := map[string]func(t *testing.T, value interface{}){
expected := map[string]func(t *testing.T, value any){
RequestContentSize: assertFloat64(0),
RequestHost: assertString(testHostname),
RequestAddr: assertString(testHostname),
@@ -851,7 +851,7 @@ func TestLogger_AbortedRequest(t *testing.T) {
logData, err := os.ReadFile(config.FilePath)
require.NoError(t, err)
jsonData := make(map[string]interface{})
jsonData := make(map[string]any)
err = json.Unmarshal(logData, &jsonData)
require.NoError(t, err)
+14 -14
View File
@@ -96,20 +96,6 @@ func (c *Capture) Reset(next http.Handler) http.Handler {
})
}
func (c *Capture) renew(rw http.ResponseWriter, req *http.Request) (http.ResponseWriter, *http.Request) {
ctx := context.WithValue(req.Context(), capturedData, c)
newReq := req.WithContext(ctx)
if newReq.Body != nil {
readCounter := &readCounter{source: newReq.Body}
c.rr = readCounter
newReq.Body = readCounter
}
c.crw = &captureResponseWriter{rw: rw}
return c.crw, newReq
}
func (c *Capture) ResponseSize() int64 {
return c.crw.Size()
}
@@ -127,6 +113,20 @@ func (c *Capture) RequestSize() int64 {
return c.rr.size
}
func (c *Capture) renew(rw http.ResponseWriter, req *http.Request) (http.ResponseWriter, *http.Request) {
ctx := context.WithValue(req.Context(), capturedData, c)
newReq := req.WithContext(ctx)
if newReq.Body != nil {
readCounter := &readCounter{source: newReq.Body}
c.rr = readCounter
newReq.Body = readCounter
}
c.crw = &captureResponseWriter{rw: rw}
return c.crw, newReq
}
type readCounter struct {
// source ReadCloser from where the request body is read.
source io.ReadCloser
+4 -4
View File
@@ -167,6 +167,10 @@ func (c *compress) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
c.chooseHandler(c.getCompressionEncoding(acceptEncoding), rw, req)
}
func (c *compress) GetTracingInformation() (string, string) {
return c.name, typeName
}
func (c *compress) chooseHandler(typ string, rw http.ResponseWriter, req *http.Request) {
switch typ {
case zstdName:
@@ -180,10 +184,6 @@ func (c *compress) chooseHandler(typ string, rw http.ResponseWriter, req *http.R
}
}
func (c *compress) GetTracingInformation() (string, string) {
return c.name, typeName
}
func (c *compress) newGzipHandler() (http.Handler, error) {
var wrapper func(http.Handler) http.HandlerFunc
var err error
@@ -156,6 +156,7 @@ func (c *CompressionHandler) putCompressionWriter(writer *compressionWriterWrapp
type compressionWriterWrapper struct {
CompressionWriter
algo string
}
@@ -874,10 +874,7 @@ func Test_FlushExcludedContentTypes(t *testing.T) {
for len(tb) > 0 {
// Write 100 bytes per run
// Detection should not be affected (we send 100 bytes)
toWrite := 100
if toWrite > len(tb) {
toWrite = len(tb)
}
toWrite := min(100, len(tb))
_, err := rw.Write(tb[:toWrite])
require.NoError(t, err)
@@ -998,10 +995,7 @@ func Test_FlushIncludedContentTypes(t *testing.T) {
for len(tb) > 0 {
// Write 100 bytes per run
// Detection should not be affected (we send 100 bytes)
toWrite := 100
if toWrite > len(tb) {
toWrite = len(tb)
}
toWrite := min(100, len(tb))
_, err := rw.Write(tb[:toWrite])
require.NoError(t, err)
+17 -22
View File
@@ -4,6 +4,7 @@ import (
"bufio"
"context"
"fmt"
"maps"
"net"
"net/http"
"net/url"
@@ -198,16 +199,6 @@ func (cc *codeCatcher) Header() http.Header {
return cc.headerMap
}
func (cc *codeCatcher) getCode() int {
return cc.code
}
// isFilteredCode returns whether the codeCatcher received a response code among the ones it is watching,
// and for which the response should be deferred to the error handler.
func (cc *codeCatcher) isFilteredCode() bool {
return cc.caughtFilteredCode
}
func (cc *codeCatcher) Write(buf []byte) (int, error) {
// If WriteHeader was already called from the caller, this is a NOOP.
// Otherwise, cc.code is actually a 200 here.
@@ -233,9 +224,7 @@ func (cc *codeCatcher) WriteHeader(code int) {
if code >= 100 && code <= 199 {
// Multiple informational status codes can be used,
// so here the copy is not appending the values to not repeat them.
for k, v := range cc.Header() {
cc.responseWriter.Header()[k] = v
}
maps.Copy(cc.responseWriter.Header(), cc.Header())
cc.responseWriter.WriteHeader(code)
return
@@ -253,9 +242,8 @@ func (cc *codeCatcher) WriteHeader(code int) {
// The copy is not appending the values,
// to not repeat them in case any informational status code has been written.
for k, v := range cc.Header() {
cc.responseWriter.Header()[k] = v
}
maps.Copy(cc.responseWriter.Header(), cc.Header())
cc.responseWriter.WriteHeader(cc.code)
cc.headersSent = true
}
@@ -288,6 +276,16 @@ func (cc *codeCatcher) Flush() {
}
}
func (cc *codeCatcher) getCode() int {
return cc.code
}
// isFilteredCode returns whether the codeCatcher received a response code among the ones it is watching,
// and for which the response should be deferred to the error handler.
func (cc *codeCatcher) isFilteredCode() bool {
return cc.caughtFilteredCode
}
// codeModifier forwards a response back to the client,
// while enforcing a given response code.
type codeModifier struct {
@@ -343,17 +341,14 @@ func (r *codeModifier) WriteHeader(code int) {
if code >= 100 && code <= 199 {
// Multiple informational status codes can be used,
// so here the copy is not appending the values to not repeat them.
for k, v := range r.headerMap {
r.responseWriter.Header()[k] = v
}
maps.Copy(r.responseWriter.Header(), r.headerMap)
r.responseWriter.WriteHeader(code)
return
}
for k, v := range r.headerMap {
r.responseWriter.Header()[k] = v
}
maps.Copy(r.responseWriter.Header(), r.headerMap)
r.responseWriter.WriteHeader(r.code)
r.headerSent = true
}
@@ -84,18 +84,11 @@ func NewXForwarded(insecure bool, trustedIPs []string, connectionHeaders []strin
}, nil
}
func (x *XForwarded) isTrustedIP(ip string) bool {
if x.ipChecker == nil {
return false
}
return x.ipChecker.IsAuthorized(ip) == nil
}
// removeIPv6Zone removes the zone if the given IP is an ipv6 address and it has {zone} information in it,
// like "[fe80::d806:a55d:eb1b:49cc%vEthernet (vmxnet3 Ethernet Adapter - Virtual Switch)]:64692".
func removeIPv6Zone(clientIP string) string {
if idx := strings.Index(clientIP, "%"); idx != -1 {
return clientIP[:idx]
if before, _, found := strings.Cut(clientIP, "%"); found {
return before
}
return clientIP
}
@@ -141,6 +134,32 @@ func forwardedPort(req *http.Request) string {
return "80"
}
// ServeHTTP implements http.Handler.
func (x *XForwarded) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !x.insecure && !x.isTrustedIP(r.RemoteAddr) {
for _, h := range xHeaders {
unsafeHeader(r.Header).Del(h)
}
}
x.rewrite(r)
x.removeConnectionHeaders(r)
if x.notAppendXForwardedFor {
r = r.WithContext(httputil.SetNotAppendXFF(r.Context()))
}
x.next.ServeHTTP(w, r)
}
func (x *XForwarded) isTrustedIP(ip string) bool {
if x.ipChecker == nil {
return false
}
return x.ipChecker.IsAuthorized(ip) == nil
}
func (x *XForwarded) rewrite(outreq *http.Request) {
if clientIP, _, err := net.SplitHostPort(outreq.RemoteAddr); err == nil {
clientIP = removeIPv6Zone(clientIP)
@@ -189,25 +208,6 @@ func (x *XForwarded) rewrite(outreq *http.Request) {
}
}
// ServeHTTP implements http.Handler.
func (x *XForwarded) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !x.insecure && !x.isTrustedIP(r.RemoteAddr) {
for _, h := range xHeaders {
unsafeHeader(r.Header).Del(h)
}
}
x.rewrite(r)
x.removeConnectionHeaders(r)
if x.notAppendXForwardedFor {
r = r.WithContext(httputil.SetNotAppendXFF(r.Context()))
}
x.next.ServeHTTP(w, r)
}
func (x *XForwarded) removeConnectionHeaders(req *http.Request) {
var reqUpType string
if httpguts.HeaderValuesContainsToken(req.Header[connection], upgrade) {
@@ -1,6 +1,7 @@
package headermodifier
import (
"maps"
"net/http"
"net/http/httptest"
"testing"
@@ -105,9 +106,7 @@ func TestRequestHeaderModifier(t *testing.T) {
handler := NewRequestHeaderModifier(t.Context(), next, test.config, "foo-request-header-modifier")
req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost", nil)
for h, v := range test.requestHeaders {
req.Header[h] = v
}
maps.Copy(req.Header, test.requestHeaders)
resp := httptest.NewRecorder()
handler.ServeHTTP(resp, req)
@@ -1,6 +1,7 @@
package headermodifier
import (
"maps"
"net/http"
"net/http/httptest"
"testing"
@@ -107,9 +108,7 @@ func TestResponseHeaderModifier(t *testing.T) {
req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost", nil)
resp := httptest.NewRecorder()
for k, v := range test.responseHeaders {
resp.Header()[k] = v
}
maps.Copy(resp.Header(), test.responseHeaders)
handler.ServeHTTP(resp, req)
+21 -21
View File
@@ -64,27 +64,6 @@ func (s *Header) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
}
}
// modifyCustomRequestHeaders sets or deletes custom request headers.
func (s *Header) modifyCustomRequestHeaders(req *http.Request) {
// Loop through Custom request headers
for header, value := range s.headers.CustomRequestHeaders {
switch {
// Handling https://github.com/golang/go/commit/ecdbffd4ec68b509998792f120868fec319de59b.
case value == "" && header == forward.XForwardedFor:
req.Header[header] = nil
case value == "":
req.Header.Del(header)
case strings.EqualFold(header, "Host"):
req.Host = value
default:
req.Header.Set(header, value)
}
}
}
// PostRequestModifyResponseHeaders set or delete response headers.
// This method is called AFTER the response is generated from the backend
// and can merge/override headers from the backend response.
@@ -134,6 +113,27 @@ func (s *Header) PostRequestModifyResponseHeaders(res *http.Response) error {
return nil
}
// modifyCustomRequestHeaders sets or deletes custom request headers.
func (s *Header) modifyCustomRequestHeaders(req *http.Request) {
// Loop through Custom request headers
for header, value := range s.headers.CustomRequestHeaders {
switch {
// Handling https://github.com/golang/go/commit/ecdbffd4ec68b509998792f120868fec319de59b.
case value == "" && header == forward.XForwardedFor:
req.Header[header] = nil
case value == "":
req.Header.Del(header)
case strings.EqualFold(header, "Host"):
req.Host = value
default:
req.Header.Set(header, value)
}
}
}
// processCorsHeaders processes the incoming request,
// and returns if it is a preflight request.
// If not a preflight, it handles the preRequestModifyCorsResponseHeaders.
@@ -65,7 +65,7 @@ func DetailedTracingEnabled(ctx context.Context) bool {
}
// SetStatusErrorf flags the span as in error and log an event.
func SetStatusErrorf(ctx context.Context, format string, args ...interface{}) {
func SetStatusErrorf(ctx context.Context, format string, args ...any) {
if span := trace.SpanFromContext(ctx); span != nil {
span.SetStatus(codes.Error, fmt.Sprintf(format, args...))
}
@@ -13,6 +13,7 @@ func newStatusCodeRecorder(rw http.ResponseWriter, status int) *statusCodeRecord
type statusCodeRecorder struct {
http.ResponseWriter
status int
}
+4 -4
View File
@@ -7,14 +7,14 @@ import (
)
type Rediser interface {
Eval(ctx context.Context, script string, keys []string, args ...interface{}) *redis.Cmd
EvalSha(ctx context.Context, sha1 string, keys []string, args ...interface{}) *redis.Cmd
Eval(ctx context.Context, script string, keys []string, args ...any) *redis.Cmd
EvalSha(ctx context.Context, sha1 string, keys []string, args ...any) *redis.Cmd
ScriptExists(ctx context.Context, hashes ...string) *redis.BoolSliceCmd
ScriptLoad(ctx context.Context, script string) *redis.StringCmd
Del(ctx context.Context, keys ...string) *redis.IntCmd
EvalRO(ctx context.Context, script string, keys []string, args ...interface{}) *redis.Cmd
EvalShaRO(ctx context.Context, sha1 string, keys []string, args ...interface{}) *redis.Cmd
EvalRO(ctx context.Context, script string, keys []string, args ...any) *redis.Cmd
EvalShaRO(ctx context.Context, sha1 string, keys []string, args ...any) *redis.Cmd
}
//nolint:dupword
+1 -4
View File
@@ -61,10 +61,7 @@ func New(ctx context.Context, next http.Handler, config dynamic.RateLimit, name
return nil, fmt.Errorf("getting source extractor: %w", err)
}
burst := config.Burst
if burst < 1 {
burst = 1
}
burst := max(config.Burst, 1)
period := time.Duration(config.Period)
if period < 0 {
@@ -300,11 +300,8 @@ func TestInMemoryRateLimit(t *testing.T) {
stop := time.Now()
elapsed := stop.Sub(start)
burst := test.config.Burst
if burst < 1 {
// actual default value
burst = 1
}
// actual default value if burst < 1
burst := max(test.config.Burst, 1)
period := time.Duration(test.config.Period)
if period == 0 {
@@ -510,11 +507,8 @@ func TestRedisRateLimit(t *testing.T) {
stop := time.Now()
elapsed := stop.Sub(start)
burst := test.config.Burst
if burst < 1 {
// actual default value
burst = 1
}
// actual default value
burst := max(test.config.Burst, 1)
period := time.Duration(test.config.Period)
if period == 0 {
@@ -570,7 +564,7 @@ func newMockRedisClient(ttl int) Rediser {
}
}
func (m *mockRedisClient) EvalSha(ctx context.Context, _ string, keys []string, args ...interface{}) *redis.Cmd {
func (m *mockRedisClient) EvalSha(ctx context.Context, _ string, keys []string, args ...any) *redis.Cmd {
state := lua.NewState()
defer state.Close()
@@ -641,7 +635,7 @@ func (m *mockRedisClient) EvalSha(ctx context.Context, _ string, keys []string,
return cmd
}
var resultSlice []interface{}
var resultSlice []any
resultTable.ForEach(func(_ lua.LValue, value lua.LValue) {
valueNbr, ok := value.(lua.LNumber)
if !ok {
@@ -661,7 +655,7 @@ func (m *mockRedisClient) EvalSha(ctx context.Context, _ string, keys []string,
return cmd
}
func (m *mockRedisClient) Eval(ctx context.Context, script string, keys []string, args ...interface{}) *redis.Cmd {
func (m *mockRedisClient) Eval(ctx context.Context, script string, keys []string, args ...any) *redis.Cmd {
return m.EvalSha(ctx, script, keys, args...)
}
@@ -677,11 +671,11 @@ func (m *mockRedisClient) Del(ctx context.Context, keys ...string) *redis.IntCmd
return nil
}
func (m *mockRedisClient) EvalRO(ctx context.Context, script string, keys []string, args ...interface{}) *redis.Cmd {
func (m *mockRedisClient) EvalRO(ctx context.Context, script string, keys []string, args ...any) *redis.Cmd {
return nil
}
func (m *mockRedisClient) EvalShaRO(ctx context.Context, sha1 string, keys []string, args ...interface{}) *redis.Cmd {
func (m *mockRedisClient) EvalShaRO(ctx context.Context, sha1 string, keys []string, args ...any) *redis.Cmd {
return nil
}
+2 -2
View File
@@ -91,7 +91,7 @@ func (r *redisLimiter) evaluateScript(ctx context.Context, key string) (bool, *t
return true, nil, nil
}
params := []interface{}{
params := []any{
float64(r.rate / 1000000),
r.burst,
r.ttl,
@@ -103,7 +103,7 @@ func (r *redisLimiter) evaluateScript(ctx context.Context, key string) (bool, *t
return false, nil, fmt.Errorf("running script: %w", err)
}
values := v.([]interface{})
values := v.([]any)
ok, err := strconv.ParseBool(values[0].(string))
if err != nil {
return false, nil, fmt.Errorf("parsing ok value from redis rate lua script: %w", err)
+1 -1
View File
@@ -56,7 +56,7 @@ func recoverFunc(rw recoveryResponseWriter, req *http.Request) {
// https://github.com/golang/go/blob/a0d6420d8be2ae7164797051ec74fa2a2df466a1/src/net/http/server.go#L1761-L1775
// https://github.com/golang/go/blob/c33153f7b416c03983324b3e8f869ce1116d84bc/src/net/http/httputil/reverseproxy.go#L284
func shouldLogPanic(panicValue interface{}) bool {
func shouldLogPanic(panicValue any) bool {
//nolint:errorlint // false-positive because panicValue is an interface.
return panicValue != nil && panicValue != http.ErrAbortHandler
}
+2 -4
View File
@@ -5,6 +5,7 @@ import (
"context"
"fmt"
"io"
"maps"
"math"
"net"
"net/http"
@@ -249,10 +250,7 @@ func (r *responseWriter) WriteHeader(code int) {
// to write headers to the backend : we are not going to perform any further retry.
// So it is now safe to alter current response headers with headers collected during
// the latest try before writing headers to client.
headers := r.responseWriter.Header()
for header, value := range r.headers {
headers[header] = value
}
maps.Copy(r.responseWriter.Header(), r.headers)
r.responseWriter.WriteHeader(code)
+2 -14
View File
@@ -184,13 +184,7 @@ func header(tree *matchersTree, headers ...string) error {
key, value := http.CanonicalHeaderKey(headers[0]), headers[1]
tree.matcher = func(req *http.Request) bool {
for _, headerValue := range req.Header[key] {
if headerValue == value {
return true
}
}
return false
return slices.Contains(req.Header[key], value)
}
return nil
@@ -205,13 +199,7 @@ func headerRegexp(tree *matchersTree, headers ...string) error {
}
tree.matcher = func(req *http.Request) bool {
for _, headerValue := range req.Header[key] {
if re.MatchString(headerValue) {
return true
}
}
return false
return slices.ContainsFunc(req.Header[key], re.MatchString)
}
return nil
+2 -1
View File
@@ -22,7 +22,8 @@ type MatcherFunc func(*http.Request) bool
// Muxer handles routing with rules.
type Muxer struct {
routes routes
routes routes
parser SyntaxParser
defaultHandler http.Handler
}
+2 -7
View File
@@ -3,6 +3,7 @@ package tcp
import (
"fmt"
"regexp"
"slices"
"strings"
"unicode/utf8"
@@ -37,13 +38,7 @@ func alpn(tree *matchersTree, protos ...string) error {
}
tree.matcher = func(meta ConnData) bool {
for _, alpnProto := range meta.alpnProtos {
if alpnProto == proto {
return true
}
}
return false
return slices.Contains(meta.alpnProtos, proto)
}
return nil
+3 -4
View File
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"regexp"
"slices"
"strconv"
"strings"
@@ -56,10 +57,8 @@ func alpnV2(tree *matchersTree, protos ...string) error {
tree.matcher = func(meta ConnData) bool {
for _, proto := range meta.alpnProtos {
for _, filter := range protos {
if proto == filter {
return true
}
if slices.Contains(protos, proto) {
return true
}
}
+1 -1
View File
@@ -121,7 +121,7 @@ func GetRulePriority(rule string) int {
// AddRoute adds a new route, associated to the given handler, at the given
// priority, to the muxer.
func (m *Muxer) AddRoute(rule string, syntax string, priority int, handler tcp.Handler) error {
var parse interface{}
var parse any
var err error
var matcherFuncs map[string]func(*matchersTree, ...string) error
+2 -2
View File
@@ -7,10 +7,10 @@ import (
func NewAWSWrapper(logger zerolog.Logger) logging.LoggerFunc {
if logger.GetLevel() > zerolog.DebugLevel {
return func(classification logging.Classification, format string, args ...interface{}) {}
return func(classification logging.Classification, format string, args ...any) {}
}
return func(classification logging.Classification, format string, args ...interface{}) {
return func(classification logging.Classification, format string, args ...any) {
logger.Debug().CallerSkipFrame(2).MsgFunc(msgFunc(args...))
}
}
+2 -2
View File
@@ -10,10 +10,10 @@ func NewElasticLogger(logger zerolog.Logger) *ElasticLogger {
return &ElasticLogger{logger: logger}
}
func (l ElasticLogger) Debugf(format string, args ...interface{}) {
func (l ElasticLogger) Debugf(format string, args ...any) {
l.logger.Debug().CallerSkipFrame(1).Msgf(format, args...)
}
func (l ElasticLogger) Errorf(format string, args ...interface{}) {
func (l ElasticLogger) Errorf(format string, args ...any) {
l.logger.Error().CallerSkipFrame(1).Msgf(format, args...)
}
+2 -2
View File
@@ -7,10 +7,10 @@ import (
func NewGoKitWrapper(logger zerolog.Logger) kitlog.LoggerFunc {
if logger.GetLevel() > zerolog.DebugLevel {
return func(args ...interface{}) error { return nil }
return func(args ...any) error { return nil }
}
return func(args ...interface{}) error {
return func(args ...any) error {
logger.Debug().CallerSkipFrame(2).MsgFunc(msgFunc(args...))
return nil
}
+5 -5
View File
@@ -21,26 +21,26 @@ func NewRetryableHTTPLogger(logger zerolog.Logger) *RetryableHTTPLogger {
}
// Error starts a new message with error level.
func (l RetryableHTTPLogger) Error(msg string, keysAndValues ...interface{}) {
func (l RetryableHTTPLogger) Error(msg string, keysAndValues ...any) {
logWithLevel(l.logger.Error().CallerSkipFrame(2), msg, keysAndValues...)
}
// Info starts a new message with info level.
func (l RetryableHTTPLogger) Info(msg string, keysAndValues ...interface{}) {
func (l RetryableHTTPLogger) Info(msg string, keysAndValues ...any) {
logWithLevel(l.logger.Info().CallerSkipFrame(2), msg, keysAndValues...)
}
// Debug starts a new message with debug level.
func (l RetryableHTTPLogger) Debug(msg string, keysAndValues ...interface{}) {
func (l RetryableHTTPLogger) Debug(msg string, keysAndValues ...any) {
logWithLevel(l.logger.Debug().CallerSkipFrame(2), msg, keysAndValues...)
}
// Warn starts a new message with warn level.
func (l RetryableHTTPLogger) Warn(msg string, keysAndValues ...interface{}) {
func (l RetryableHTTPLogger) Warn(msg string, keysAndValues ...any) {
logWithLevel(l.logger.Warn().CallerSkipFrame(2), msg, keysAndValues...)
}
func logWithLevel(ev *zerolog.Event, msg string, kvs ...interface{}) {
func logWithLevel(ev *zerolog.Event, msg string, kvs ...any) {
if len(kvs)%2 == 0 {
for i := 0; i < len(kvs)-1; i += 2 {
// The first item of the pair (the key) is supposed to be a string.
+4 -4
View File
@@ -12,18 +12,18 @@ func NewInstanaLogger(logger zerolog.Logger) *InstanaLogger {
return &InstanaLogger{logger: logger}
}
func (l InstanaLogger) Debug(args ...interface{}) {
func (l InstanaLogger) Debug(args ...any) {
l.logger.Debug().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
}
func (l InstanaLogger) Info(args ...interface{}) {
func (l InstanaLogger) Info(args ...any) {
l.logger.Info().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
}
func (l InstanaLogger) Warn(args ...interface{}) {
func (l InstanaLogger) Warn(args ...any) {
l.logger.Warn().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
}
func (l InstanaLogger) Error(args ...interface{}) {
func (l InstanaLogger) Error(args ...any) {
l.logger.Error().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
}
+9 -9
View File
@@ -12,38 +12,38 @@ func NewLogrusWrapper(logger zerolog.Logger) *LogrusStdWrapper {
return &LogrusStdWrapper{logger: logger}
}
func (l LogrusStdWrapper) Print(args ...interface{}) {
func (l LogrusStdWrapper) Print(args ...any) {
l.logger.Debug().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
}
func (l LogrusStdWrapper) Printf(s string, args ...interface{}) {
func (l LogrusStdWrapper) Printf(s string, args ...any) {
l.logger.Debug().CallerSkipFrame(1).Msgf(s, args...)
}
func (l LogrusStdWrapper) Println(args ...interface{}) {
func (l LogrusStdWrapper) Println(args ...any) {
l.logger.Debug().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
}
func (l LogrusStdWrapper) Fatal(args ...interface{}) {
func (l LogrusStdWrapper) Fatal(args ...any) {
l.logger.Fatal().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
}
func (l LogrusStdWrapper) Fatalf(s string, args ...interface{}) {
func (l LogrusStdWrapper) Fatalf(s string, args ...any) {
l.logger.Fatal().CallerSkipFrame(1).Msgf(s, args...)
}
func (l LogrusStdWrapper) Fatalln(args ...interface{}) {
func (l LogrusStdWrapper) Fatalln(args ...any) {
l.logger.Fatal().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
}
func (l LogrusStdWrapper) Panic(args ...interface{}) {
func (l LogrusStdWrapper) Panic(args ...any) {
l.logger.Panic().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
}
func (l LogrusStdWrapper) Panicf(s string, args ...interface{}) {
func (l LogrusStdWrapper) Panicf(s string, args ...any) {
l.logger.Panic().CallerSkipFrame(1).Msgf(s, args...)
}
func (l LogrusStdWrapper) Panicln(args ...interface{}) {
func (l LogrusStdWrapper) Panicln(args ...any) {
l.logger.Panic().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
}
+4 -4
View File
@@ -10,18 +10,18 @@ func NewOxyWrapper(logger zerolog.Logger) *OxyWrapper {
return &OxyWrapper{logger: logger}
}
func (l OxyWrapper) Debug(s string, i ...interface{}) {
func (l OxyWrapper) Debug(s string, i ...any) {
l.logger.Debug().CallerSkipFrame(1).Msgf(s, i...)
}
func (l OxyWrapper) Info(s string, i ...interface{}) {
func (l OxyWrapper) Info(s string, i ...any) {
l.logger.Info().CallerSkipFrame(1).Msgf(s, i...)
}
func (l OxyWrapper) Warn(s string, i ...interface{}) {
func (l OxyWrapper) Warn(s string, i ...any) {
l.logger.Warn().CallerSkipFrame(1).Msgf(s, i...)
}
func (l OxyWrapper) Error(s string, i ...interface{}) {
func (l OxyWrapper) Error(s string, i ...any) {
l.logger.Error().CallerSkipFrame(1).Msgf(s, i...)
}
+5 -5
View File
@@ -36,16 +36,16 @@ func (c MultiCounterWithHeaders) With(headers http.Header, labelValues ...string
return next
}
// NewCounterWithNoopHeaders returns a CounterWithNoopHeaders.
func NewCounterWithNoopHeaders(counter metrics.Counter) CounterWithNoopHeaders {
return CounterWithNoopHeaders{counter: counter}
}
// CounterWithNoopHeaders is a counter that satisfies CounterWithHeaders but ignores the given http.Header.
type CounterWithNoopHeaders struct {
counter metrics.Counter
}
// NewCounterWithNoopHeaders returns a CounterWithNoopHeaders.
func NewCounterWithNoopHeaders(counter metrics.Counter) CounterWithNoopHeaders {
return CounterWithNoopHeaders{counter: counter}
}
// Add adds the given delta value to the counter value.
func (c CounterWithNoopHeaders) Add(delta float64) {
c.counter.Add(delta)
+1 -1
View File
@@ -506,7 +506,7 @@ func (lvs otelLabelNamesValues) With(labelValues ...string) otelLabelNamesValues
// to the native attribute.KeyValue.
func (lvs otelLabelNamesValues) ToLabels() []attribute.KeyValue {
labels := make([]attribute.KeyValue, len(lvs)/2)
for i := 0; i < len(labels); i++ {
for i := range labels {
labels[i] = attribute.String(lvs[2*i], lvs[2*i+1])
}
return labels
+70 -70
View File
@@ -29,6 +29,15 @@ type Backend interface {
Setup(ctx context.Context, serviceName string, sampleRate float64, resourceAttributes map[string]string) (trace.Tracer, io.Closer, error)
}
// Tracer is trace.Tracer with additional properties.
type Tracer struct {
trace.Tracer
safeQueryParams []string
capturedRequestHeaders []string
capturedResponseHeaders []string
}
// NewTracing Creates a Tracing.
func NewTracing(ctx context.Context, conf *static.Tracing) (*Tracer, io.Closer, error) {
var backend Backend
@@ -57,76 +66,6 @@ func NewTracing(ctx context.Context, conf *static.Tracing) (*Tracer, io.Closer,
return NewTracer(tr, conf.CapturedRequestHeaders, conf.CapturedResponseHeaders, conf.SafeQueryParams), closer, nil
}
// TracerFromContext extracts the trace.Tracer from the given context.
func TracerFromContext(ctx context.Context) *Tracer {
// Prevent picking trace.noopSpan tracer.
if !trace.SpanContextFromContext(ctx).IsValid() {
return nil
}
span := trace.SpanFromContext(ctx)
if span != nil && span.TracerProvider() != nil {
tracer := span.TracerProvider().Tracer("github.com/traefik/traefik")
if tracer, ok := tracer.(*Tracer); ok {
return tracer
}
return nil
}
return nil
}
// ExtractCarrierIntoContext reads cross-cutting concerns from the carrier into a Context.
func ExtractCarrierIntoContext(ctx context.Context, headers http.Header) context.Context {
propagator := otel.GetTextMapPropagator()
return propagator.Extract(ctx, propagation.HeaderCarrier(headers))
}
// InjectContextIntoCarrier sets cross-cutting concerns from the request context into the request headers.
func InjectContextIntoCarrier(req *http.Request) {
propagator := otel.GetTextMapPropagator()
propagator.Inject(req.Context(), propagation.HeaderCarrier(req.Header))
}
// Span is trace.Span wrapping the Traefik TracerProvider.
type Span struct {
trace.Span
tracerProvider *TracerProvider
}
// TracerProvider returns the span's TraceProvider.
func (s Span) TracerProvider() trace.TracerProvider {
return s.tracerProvider
}
// TracerProvider is trace.TracerProvider wrapping the Traefik Tracer implementation.
type TracerProvider struct {
trace.TracerProvider
tracer *Tracer
}
// Tracer returns the trace.Tracer for the given options.
// It returns specifically the Traefik Tracer when requested.
func (t TracerProvider) Tracer(name string, options ...trace.TracerOption) trace.Tracer {
if name == "github.com/traefik/traefik" {
return t.tracer
}
return t.TracerProvider.Tracer(name, options...)
}
// Tracer is trace.Tracer with additional properties.
type Tracer struct {
trace.Tracer
safeQueryParams []string
capturedRequestHeaders []string
capturedResponseHeaders []string
}
// NewTracer builds and configures a new Tracer.
func NewTracer(tracer trace.Tracer, capturedRequestHeaders, capturedResponseHeaders, safeQueryParams []string) *Tracer {
return &Tracer{
@@ -299,6 +238,67 @@ func (t *Tracer) safeURL(originalURL *url.URL) *url.URL {
return &redactedURL
}
// Span is trace.Span wrapping the Traefik TracerProvider.
type Span struct {
trace.Span
tracerProvider *TracerProvider
}
// TracerProvider returns the span's TraceProvider.
func (s Span) TracerProvider() trace.TracerProvider {
return s.tracerProvider
}
// TracerProvider is trace.TracerProvider wrapping the Traefik Tracer implementation.
type TracerProvider struct {
trace.TracerProvider
tracer *Tracer
}
// Tracer returns the trace.Tracer for the given options.
// It returns specifically the Traefik Tracer when requested.
func (t TracerProvider) Tracer(name string, options ...trace.TracerOption) trace.Tracer {
if name == "github.com/traefik/traefik" {
return t.tracer
}
return t.TracerProvider.Tracer(name, options...)
}
// TracerFromContext extracts the trace.Tracer from the given context.
func TracerFromContext(ctx context.Context) *Tracer {
// Prevent picking trace.noopSpan tracer.
if !trace.SpanContextFromContext(ctx).IsValid() {
return nil
}
span := trace.SpanFromContext(ctx)
if span != nil && span.TracerProvider() != nil {
tracer := span.TracerProvider().Tracer("github.com/traefik/traefik")
if tracer, ok := tracer.(*Tracer); ok {
return tracer
}
return nil
}
return nil
}
// ExtractCarrierIntoContext reads cross-cutting concerns from the carrier into a Context.
func ExtractCarrierIntoContext(ctx context.Context, headers http.Header) context.Context {
propagator := otel.GetTextMapPropagator()
return propagator.Extract(ctx, propagation.HeaderCarrier(headers))
}
// InjectContextIntoCarrier sets cross-cutting concerns from the request context into the request headers.
func InjectContextIntoCarrier(req *http.Request) {
propagator := otel.GetTextMapPropagator()
propagator.Inject(req.Context(), propagation.HeaderCarrier(req.Header))
}
func proto(proto string) string {
switch proto {
case "HTTP/1.0":
@@ -492,6 +492,8 @@ func resourceAttributes(traces ptrace.Traces) map[string]string {
}
// mainSpan gets the main span from traces (assumes single span for testing).
//
//nolint:unqueryvet // False positive: This is OTel trace iteration, not SQLBoiler.
func mainSpan(traces ptrace.Traces) ptrace.Span {
for _, resourceSpans := range traces.ResourceSpans().All() {
for _, scopeSpans := range resourceSpans.ScopeSpans().All() {
+2 -2
View File
@@ -18,7 +18,7 @@ type pluginMiddleware interface {
}
type middlewareBuilder interface {
newMiddleware(config map[string]interface{}, middlewareName string) (pluginMiddleware, error)
newMiddleware(config map[string]any, middlewareName string) (pluginMiddleware, error)
}
// Builder is a plugin builder.
@@ -110,7 +110,7 @@ func NewBuilder(manager *Manager, plugins map[string]Descriptor, localPlugins ma
}
// Build builds a middleware plugin.
func (b Builder) Build(pName string, config map[string]interface{}, middlewareName string) (Constructor, error) {
func (b Builder) Build(pName string, config map[string]any, middlewareName string) (Constructor, error) {
if b.middlewareBuilders == nil {
return nil, fmt.Errorf("no plugin definitions in the static configuration: %s", pName)
}
+1 -1
View File
@@ -51,7 +51,7 @@ func TestPluginManager_ReadManifest(t *testing.T) {
Type: "middleware",
Import: "github.com/test/plugin",
Summary: "A test plugin",
TestData: map[string]interface{}{
TestData: map[string]any{
"test": "data",
},
}
+2 -2
View File
@@ -42,7 +42,7 @@ func newWasmMiddlewareBuilder(goPath, moduleName, wasmPath string, settings Sett
return &wasmMiddlewareBuilder{path: path, cache: cache, settings: settings}, nil
}
func (b wasmMiddlewareBuilder) newMiddleware(config map[string]interface{}, middlewareName string) (pluginMiddleware, error) {
func (b wasmMiddlewareBuilder) newMiddleware(config map[string]any, middlewareName string) (pluginMiddleware, error) {
return &WasmMiddleware{
middlewareName: middlewareName,
config: reflect.ValueOf(config),
@@ -114,7 +114,7 @@ func (b *wasmMiddlewareBuilder) buildMiddleware(ctx context.Context, next http.H
i := cfg.Interface()
if i != nil {
config, ok := i.(map[string]interface{})
config, ok := i.(map[string]any)
if !ok {
return nil, nil, fmt.Errorf("could not type assert config: %T", i)
}
+9 -9
View File
@@ -27,12 +27,12 @@ func TestSettingsWithoutSocket(t *testing.T) {
testCases := []struct {
desc string
getSettings func(t *testing.T) (Settings, map[string]interface{})
getSettings func(t *testing.T) (Settings, map[string]any)
expected string
}{
{
desc: "mounts path",
getSettings: func(t *testing.T) (Settings, map[string]interface{}) {
getSettings: func(t *testing.T) (Settings, map[string]any) {
t.Helper()
tempDir := t.TempDir()
@@ -42,7 +42,7 @@ func TestSettingsWithoutSocket(t *testing.T) {
return Settings{Mounts: []string{
tempDir,
}}, map[string]interface{}{
}}, map[string]any{
"file": filePath,
}
},
@@ -50,7 +50,7 @@ func TestSettingsWithoutSocket(t *testing.T) {
},
{
desc: "mounts src to dest",
getSettings: func(t *testing.T) (Settings, map[string]interface{}) {
getSettings: func(t *testing.T) (Settings, map[string]any) {
t.Helper()
tempDir := t.TempDir()
@@ -60,7 +60,7 @@ func TestSettingsWithoutSocket(t *testing.T) {
return Settings{Mounts: []string{
tempDir + ":/tmp",
}}, map[string]interface{}{
}}, map[string]any{
"file": "/tmp/hello.txt",
}
},
@@ -68,11 +68,11 @@ func TestSettingsWithoutSocket(t *testing.T) {
},
{
desc: "one env",
getSettings: func(t *testing.T) (Settings, map[string]interface{}) {
getSettings: func(t *testing.T) (Settings, map[string]any) {
t.Helper()
envs := []string{"PLUGIN_TEST"}
return Settings{Envs: envs}, map[string]interface{}{
return Settings{Envs: envs}, map[string]any{
"envs": envs,
}
},
@@ -80,11 +80,11 @@ func TestSettingsWithoutSocket(t *testing.T) {
},
{
desc: "two env",
getSettings: func(t *testing.T) (Settings, map[string]interface{}) {
getSettings: func(t *testing.T) (Settings, map[string]any) {
t.Helper()
envs := []string{"PLUGIN_TEST", "PLUGIN_TEST_B"}
return Settings{Envs: envs}, map[string]interface{}{
return Settings{Envs: envs}, map[string]any{
"envs": envs,
}
},
+2 -2
View File
@@ -46,7 +46,7 @@ func newYaegiMiddlewareBuilder(i *interp.Interpreter, basePkg, imp string) (*yae
}, nil
}
func (b yaegiMiddlewareBuilder) newMiddleware(config map[string]interface{}, middlewareName string) (pluginMiddleware, error) {
func (b yaegiMiddlewareBuilder) newMiddleware(config map[string]any, middlewareName string) (pluginMiddleware, error) {
vConfig, err := b.createConfig(config)
if err != nil {
return nil, err
@@ -80,7 +80,7 @@ func (b yaegiMiddlewareBuilder) newHandler(ctx context.Context, next http.Handle
return handler, nil
}
func (b yaegiMiddlewareBuilder) createConfig(config map[string]interface{}) (reflect.Value, error) {
func (b yaegiMiddlewareBuilder) createConfig(config map[string]any) (reflect.Value, error) {
results := b.fnCreateConfig.Call(nil)
if len(results) != 1 {
return reflect.Value{}, fmt.Errorf("invalid number of return for the CreateConfig function: %d", len(results))
+1 -1
View File
@@ -138,7 +138,7 @@ func verifyMiddlewareWorks(t *testing.T, builder *yaegiMiddlewareBuilder) {
t.Helper()
// Create a middleware instance - this will call the plugin's New() function
// which uses unsafe/syscall, proving they work
middleware, err := builder.newMiddleware(map[string]interface{}{
middleware, err := builder.newMiddleware(map[string]any{
"message": "test",
}, "test-middleware")
require.NoError(t, err, "Should be able to create middleware that uses unsafe/syscall")
+3 -3
View File
@@ -25,7 +25,7 @@ type PP interface {
}
type _PP struct {
IValue interface{}
IValue any
WInit func() error
WProvide func(cfgChan chan<- json.Marshaler) error
WStop func() error
@@ -53,7 +53,7 @@ func ppSymbols() map[string]map[string]reflect.Value {
}
// BuildProvider builds a plugin's provider.
func (b Builder) BuildProvider(pName string, config map[string]interface{}) (provider.Provider, error) {
func (b Builder) BuildProvider(pName string, config map[string]any) (provider.Provider, error) {
if b.providerBuilders == nil {
return nil, fmt.Errorf("no plugin definition in the static configuration: %s", pName)
}
@@ -82,7 +82,7 @@ type Provider struct {
pp PP
}
func newProvider(builder providerBuilder, config map[string]interface{}, providerName string) (*Provider, error) {
func newProvider(builder providerBuilder, config map[string]any, providerName string) (*Provider, error) {
basePkg := builder.BasePkg
if basePkg == "" {
basePkg = strings.ReplaceAll(path.Base(builder.Import), "-", "_")
+10 -10
View File
@@ -42,16 +42,16 @@ type LocalDescriptor struct {
// Manifest The plugin manifest.
type Manifest struct {
DisplayName string `yaml:"displayName"`
Type string `yaml:"type"`
Runtime string `yaml:"runtime"`
WasmPath string `yaml:"wasmPath"`
Import string `yaml:"import"`
BasePkg string `yaml:"basePkg"`
Compatibility string `yaml:"compatibility"`
Summary string `yaml:"summary"`
UseUnsafe bool `yaml:"useUnsafe"`
TestData map[string]interface{} `yaml:"testData"`
DisplayName string `yaml:"displayName"`
Type string `yaml:"type"`
Runtime string `yaml:"runtime"`
WasmPath string `yaml:"wasmPath"`
Import string `yaml:"import"`
BasePkg string `yaml:"basePkg"`
Compatibility string `yaml:"compatibility"`
Summary string `yaml:"summary"`
UseUnsafe bool `yaml:"useUnsafe"`
TestData map[string]any `yaml:"testData"`
}
// IsYaegiPlugin returns true if the plugin is a Yaegi plugin.
+49 -53
View File
@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"io"
"maps"
"os"
"sync"
@@ -30,6 +31,52 @@ func NewLocalStore(filename string, routinesPool *safe.Pool) *LocalStore {
return store
}
// GetAccount returns ACME Account.
func (s *LocalStore) GetAccount(resolverName string) (*Account, error) {
storedData, err := s.get(resolverName)
if err != nil {
return nil, err
}
return storedData.Account, nil
}
// SaveAccount stores ACME Account.
func (s *LocalStore) SaveAccount(resolverName string, account *Account) error {
storedData, err := s.get(resolverName)
if err != nil {
return err
}
storedData.Account = account
s.save(resolverName, storedData)
return nil
}
// GetCertificates returns ACME Certificates list.
func (s *LocalStore) GetCertificates(resolverName string) ([]*CertAndStore, error) {
storedData, err := s.get(resolverName)
if err != nil {
return nil, err
}
return storedData.Certificates, nil
}
// SaveCertificates stores ACME Certificates list.
func (s *LocalStore) SaveCertificates(resolverName string, certificates []*CertAndStore) error {
storedData, err := s.get(resolverName)
if err != nil {
return err
}
storedData.Certificates = certificates
s.save(resolverName, storedData)
return nil
}
func (s *LocalStore) save(resolverName string, storedData *StoredData) {
s.lock.Lock()
defer s.lock.Unlock()
@@ -122,8 +169,7 @@ func (s *LocalStore) listenSaveAction(routinesPool *safe.Pool) {
logger.Error().Err(err).Send()
}
err = os.WriteFile(s.filename, data, 0o600)
if err != nil {
if err := os.WriteFile(s.filename, data, 0o600); err != nil {
logger.Error().Err(err).Send()
}
}
@@ -133,55 +179,5 @@ func (s *LocalStore) listenSaveAction(routinesPool *safe.Pool) {
// unSafeCopyOfStoredData creates maps copy of storedData. Is not thread safe, you should use `s.lock`.
func (s *LocalStore) unSafeCopyOfStoredData() map[string]*StoredData {
result := map[string]*StoredData{}
for k, v := range s.storedData {
result[k] = v
}
return result
}
// GetAccount returns ACME Account.
func (s *LocalStore) GetAccount(resolverName string) (*Account, error) {
storedData, err := s.get(resolverName)
if err != nil {
return nil, err
}
return storedData.Account, nil
}
// SaveAccount stores ACME Account.
func (s *LocalStore) SaveAccount(resolverName string, account *Account) error {
storedData, err := s.get(resolverName)
if err != nil {
return err
}
storedData.Account = account
s.save(resolverName, storedData)
return nil
}
// GetCertificates returns ACME Certificates list.
func (s *LocalStore) GetCertificates(resolverName string) ([]*CertAndStore, error) {
storedData, err := s.get(resolverName)
if err != nil {
return nil, err
}
return storedData.Certificates, nil
}
// SaveCertificates stores ACME Certificates list.
func (s *LocalStore) SaveCertificates(resolverName string, certificates []*CertAndStore) error {
storedData, err := s.get(resolverName)
if err != nil {
return err
}
storedData.Certificates = certificates
s.save(resolverName, storedData)
return nil
return maps.Clone(s.storedData)
}
-1
View File
@@ -1,5 +1,4 @@
//go:build !windows
// +build !windows
package acme
+2
View File
@@ -80,6 +80,7 @@ func (a *Configuration) SetDefaults() {
// CertAndStore allows mapping a TLS certificate to a TLS store.
type CertAndStore struct {
Certificate
Store string
}
@@ -129,6 +130,7 @@ type TLSChallenge struct {
// Provider holds configurations of the provider.
type Provider struct {
*Configuration
ResolverName string
Store Store `json:"store,omitempty" toml:"store,omitempty" yaml:"store,omitempty"`
+7 -7
View File
@@ -150,13 +150,6 @@ func NewProviderAggregator(conf static.Providers) *ProviderAggregator {
return p
}
func (p *ProviderAggregator) quietAddProvider(provider provider.Provider) {
err := p.AddProvider(provider)
if err != nil {
log.Error().Err(err).Msgf("Error while initializing provider %T", provider)
}
}
// AddProvider adds a provider in the providers map.
func (p *ProviderAggregator) AddProvider(provider provider.Provider) error {
err := provider.Init()
@@ -202,6 +195,13 @@ func (p *ProviderAggregator) Provide(configurationChan chan<- dynamic.Message, p
return nil
}
func (p *ProviderAggregator) quietAddProvider(provider provider.Provider) {
err := p.AddProvider(provider)
if err != nil {
log.Error().Err(err).Msgf("Error while initializing provider %T", provider)
}
}
func (p *ProviderAggregator) launchProvider(configurationChan chan<- dynamic.Message, pool *safe.Pool, prd provider.Provider) {
jsonConf, err := redactor.RemoveCredentials(prd)
if err != nil {
+2 -4
View File
@@ -401,9 +401,7 @@ func MakeDefaultRuleTemplate(defaultRule string, funcMap template.FuncMap) (*tem
defaultFuncMap := sprig.TxtFuncMap()
defaultFuncMap["normalize"] = Normalize
for k, fn := range funcMap {
defaultFuncMap[k] = fn
}
maps.Copy(defaultFuncMap, funcMap)
return template.New("defaultRule").Funcs(defaultFuncMap).Parse(defaultRule)
}
@@ -458,7 +456,7 @@ func BuildUDPRouterConfiguration(ctx context.Context, configuration *dynamic.UDP
}
// BuildRouterConfiguration builds a router configuration.
func BuildRouterConfiguration(ctx context.Context, configuration *dynamic.HTTPConfiguration, defaultRouterName string, defaultRuleTpl *template.Template, model interface{}) {
func BuildRouterConfiguration(ctx context.Context, configuration *dynamic.HTTPConfiguration, defaultRouterName string, defaultRuleTpl *template.Template, model any) {
if len(configuration.Routers) == 0 {
if len(configuration.Services) > 1 {
log.Ctx(ctx).Info().Msg("Could not create a router for the container: too many services")
@@ -24,7 +24,7 @@ func MatchLabels(labels map[string]string, expr string) (bool, error) {
NOT: notLabelFunc,
OR: orLabelFunc,
},
Functions: map[string]interface{}{
Functions: map[string]any{
"Label": labelFn,
"LabelRegex": labelRegexFn,
},
+1 -1
View File
@@ -25,7 +25,7 @@ func MatchTags(tags []string, expr string) (bool, error) {
NOT: notTagFunc,
OR: orTagFunc,
},
Functions: map[string]interface{}{
Functions: map[string]any{
"Tag": tagFn,
"TagRegex": tagRegexFn,
},
+66 -66
View File
@@ -248,6 +248,11 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
return nil
}
// Namespace returns the namespace of the ConsulCatalog provider.
func (p *Provider) Namespace() string {
return p.namespace
}
func (p *Provider) loadConfiguration(ctx context.Context, certInfo *connectCert, configurationChan chan<- dynamic.Message) error {
data, err := p.getConsulServicesData(ctx)
if err != nil {
@@ -388,12 +393,12 @@ func (p *Provider) fetchService(ctx context.Context, name string, connectEnabled
// watchServices watches for update events of the services list and statuses,
// and transmits them to the caller through the p.watchServicesChan.
func (p *Provider) watchServices(ctx context.Context) error {
servicesWatcher, err := watch.Parse(map[string]interface{}{"type": "services"})
servicesWatcher, err := watch.Parse(map[string]any{"type": "services"})
if err != nil {
return fmt.Errorf("failed to create services watcher plan: %w", err)
}
servicesWatcher.HybridHandler = func(_ watch.BlockingParamVal, _ interface{}) {
servicesWatcher.HybridHandler = func(_ watch.BlockingParamVal, _ any) {
select {
case <-ctx.Done():
case p.watchServicesChan <- struct{}{}:
@@ -402,12 +407,12 @@ func (p *Provider) watchServices(ctx context.Context) error {
}
}
checksWatcher, err := watch.Parse(map[string]interface{}{"type": "checks"})
checksWatcher, err := watch.Parse(map[string]any{"type": "checks"})
if err != nil {
return fmt.Errorf("failed to create checks watcher plan: %w", err)
}
checksWatcher.HybridHandler = func(_ watch.BlockingParamVal, _ interface{}) {
checksWatcher.HybridHandler = func(_ watch.BlockingParamVal, _ any) {
select {
case <-ctx.Done():
case p.watchServicesChan <- struct{}{}:
@@ -447,66 +452,11 @@ func (p *Provider) watchServices(ctx context.Context) error {
}
}
func rootsWatchHandler(ctx context.Context, dest chan<- []string) func(watch.BlockingParamVal, interface{}) {
return func(_ watch.BlockingParamVal, raw interface{}) {
if raw == nil {
log.Ctx(ctx).Error().Msg("Root certificate watcher called with nil")
return
}
v, ok := raw.(*api.CARootList)
if !ok || v == nil {
log.Ctx(ctx).Error().Msg("Invalid result for root certificate watcher")
return
}
roots := make([]string, 0, len(v.Roots))
for _, root := range v.Roots {
roots = append(roots, root.RootCertPEM)
}
select {
case <-ctx.Done():
case dest <- roots:
}
}
}
type keyPair struct {
cert string
key string
}
func leafWatcherHandler(ctx context.Context, dest chan<- keyPair) func(watch.BlockingParamVal, interface{}) {
return func(_ watch.BlockingParamVal, raw interface{}) {
if raw == nil {
log.Ctx(ctx).Error().Msg("Leaf certificate watcher called with nil")
return
}
v, ok := raw.(*api.LeafCert)
if !ok || v == nil {
log.Ctx(ctx).Error().Msg("Invalid result for leaf certificate watcher")
return
}
kp := keyPair{
cert: v.CertPEM,
key: v.PrivateKeyPEM,
}
select {
case <-ctx.Done():
case dest <- kp:
}
}
}
// watchConnectTLS watches for updates of the root certificate or the leaf
// certificate, and transmits them to the caller via p.certChan.
func (p *Provider) watchConnectTLS(ctx context.Context) error {
leafChan := make(chan keyPair)
leafWatcher, err := watch.Parse(map[string]interface{}{
leafWatcher, err := watch.Parse(map[string]any{
"type": "connect_leaf",
"service": p.ServiceName,
})
@@ -516,7 +466,7 @@ func (p *Provider) watchConnectTLS(ctx context.Context) error {
leafWatcher.HybridHandler = leafWatcherHandler(ctx, leafChan)
rootsChan := make(chan []string)
rootsWatcher, err := watch.Parse(map[string]interface{}{
rootsWatcher, err := watch.Parse(map[string]any{
"type": "connect_roots",
})
if err != nil {
@@ -596,6 +546,61 @@ func (p *Provider) includesHealthStatus(status string) bool {
return false
}
func rootsWatchHandler(ctx context.Context, dest chan<- []string) func(watch.BlockingParamVal, any) {
return func(_ watch.BlockingParamVal, raw any) {
if raw == nil {
log.Ctx(ctx).Error().Msg("Root certificate watcher called with nil")
return
}
v, ok := raw.(*api.CARootList)
if !ok || v == nil {
log.Ctx(ctx).Error().Msg("Invalid result for root certificate watcher")
return
}
roots := make([]string, 0, len(v.Roots))
for _, root := range v.Roots {
roots = append(roots, root.RootCertPEM)
}
select {
case <-ctx.Done():
case dest <- roots:
}
}
}
type keyPair struct {
cert string
key string
}
func leafWatcherHandler(ctx context.Context, dest chan<- keyPair) func(watch.BlockingParamVal, any) {
return func(_ watch.BlockingParamVal, raw any) {
if raw == nil {
log.Ctx(ctx).Error().Msg("Leaf certificate watcher called with nil")
return
}
v, ok := raw.(*api.LeafCert)
if !ok || v == nil {
log.Ctx(ctx).Error().Msg("Invalid result for leaf certificate watcher")
return
}
kp := keyPair{
cert: v.CertPEM,
key: v.PrivateKeyPEM,
}
select {
case <-ctx.Done():
case dest <- kp:
}
}
}
func createClient(namespace string, endpoint *EndpointConfig) (*api.Client, error) {
config := api.Config{
Address: endpoint.Address,
@@ -647,8 +652,3 @@ func repeatSend(ctx context.Context, interval time.Duration, c chan<- struct{})
}
}
}
// Namespace returns the namespace of the ConsulCatalog provider.
func (p *Provider) Namespace() string {
return p.namespace
}
+1
View File
@@ -20,6 +20,7 @@ import (
type DynConfBuilder struct {
Shared
apiClient client.APIClient
swarm bool
}
+4 -4
View File
@@ -50,10 +50,6 @@ func (p *Provider) Init() error {
return nil
}
func (p *Provider) createClient(ctx context.Context) (*client.Client, error) {
return createClient(ctx, p.ClientConfig)
}
// Provide allows the docker provider to provide configurations to traefik using the given configuration channel.
func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error {
pool.GoCtx(func(routineCtx context.Context) {
@@ -160,6 +156,10 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
return nil
}
func (p *Provider) createClient(ctx context.Context) (*client.Client, error) {
return createClient(ctx, p.ClientConfig)
}
func (p *Provider) listContainers(ctx context.Context, dockerClient client.ContainerAPIClient) ([]dockerData, error) {
containerList, err := dockerClient.ContainerList(ctx, container.ListOptions{
All: true,
+4 -4
View File
@@ -54,10 +54,6 @@ func (p *SwarmProvider) Init() error {
return nil
}
func (p *SwarmProvider) createClient(ctx context.Context) (*client.Client, error) {
return createClient(ctx, p.ClientConfig)
}
// Provide allows the docker provider to provide configurations to traefik using the given configuration channel.
func (p *SwarmProvider) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error {
pool.GoCtx(func(routineCtx context.Context) {
@@ -154,6 +150,10 @@ func (p *SwarmProvider) Provide(configurationChan chan<- dynamic.Message, pool *
return nil
}
func (p *SwarmProvider) createClient(ctx context.Context) (*client.Client, error) {
return createClient(ctx, p.ClientConfig)
}
func (p *SwarmProvider) listServices(ctx context.Context, dockerClient client.APIClient) ([]dockerData, error) {
logger := log.Ctx(ctx)
+2
View File
@@ -12,6 +12,7 @@ import (
type fakeTasksClient struct {
dockerclient.APIClient
tasks []swarmtypes.Task
container containertypes.InspectResponse
err error
@@ -27,6 +28,7 @@ func (c *fakeTasksClient) ContainerInspect(ctx context.Context, container string
type fakeServicesClient struct {
dockerclient.APIClient
dockerVersion string
networks []networktypes.Summary
nodes []swarmtypes.Node
+35 -35
View File
@@ -104,41 +104,6 @@ func (p *Provider) Init() error {
return nil
}
func (p *Provider) createClient(ctx context.Context, logger zerolog.Logger) (*awsClient, error) {
optFns := []func(*config.LoadOptions) error{
config.WithLogger(logs.NewAWSWrapper(logger)),
}
if p.Region != "" {
optFns = append(optFns, config.WithRegion(p.Region))
} else {
logger.Info().Msg("No region provided, will retrieve region from the EC2 Metadata service")
optFns = append(optFns, config.WithEC2IMDSRegion())
}
if p.AccessKeyID != "" && p.SecretAccessKey != "" {
// From https://docs.aws.amazon.com/sdk-for-go/v2/developer-guide/configure-gosdk.html#specify-credentials-programmatically:
// "If you explicitly provide credentials, as in this example, the SDK uses only those credentials."
// this makes sure that user-defined credentials always have the highest priority
staticCreds := aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(p.AccessKeyID, p.SecretAccessKey, ""))
optFns = append(optFns, config.WithCredentialsProvider(staticCreds))
// If the access key and secret access key are not provided, config.LoadDefaultConfig
// will look for the credentials in the default credential chain.
// See https://docs.aws.amazon.com/sdk-for-go/v2/developer-guide/configure-gosdk.html#specifying-credentials.
}
cfg, err := config.LoadDefaultConfig(ctx, optFns...)
if err != nil {
return nil, err
}
return &awsClient{
ecs.NewFromConfig(cfg),
ec2.NewFromConfig(cfg),
ssm.NewFromConfig(cfg),
}, nil
}
// Provide configuration to traefik from ECS.
func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error {
pool.GoCtx(func(routineCtx context.Context) {
@@ -185,6 +150,41 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
return nil
}
func (p *Provider) createClient(ctx context.Context, logger zerolog.Logger) (*awsClient, error) {
optFns := []func(*config.LoadOptions) error{
config.WithLogger(logs.NewAWSWrapper(logger)),
}
if p.Region != "" {
optFns = append(optFns, config.WithRegion(p.Region))
} else {
logger.Info().Msg("No region provided, will retrieve region from the EC2 Metadata service")
optFns = append(optFns, config.WithEC2IMDSRegion())
}
if p.AccessKeyID != "" && p.SecretAccessKey != "" {
// From https://docs.aws.amazon.com/sdk-for-go/v2/developer-guide/configure-gosdk.html#specify-credentials-programmatically:
// "If you explicitly provide credentials, as in this example, the SDK uses only those credentials."
// this makes sure that user-defined credentials always have the highest priority
staticCreds := aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(p.AccessKeyID, p.SecretAccessKey, ""))
optFns = append(optFns, config.WithCredentialsProvider(staticCreds))
// If the access key and secret access key are not provided, config.LoadDefaultConfig
// will look for the credentials in the default credential chain.
// See https://docs.aws.amazon.com/sdk-for-go/v2/developer-guide/configure-gosdk.html#specifying-credentials.
}
cfg, err := config.LoadDefaultConfig(ctx, optFns...)
if err != nil {
return nil, err
}
return &awsClient{
ecs.NewFromConfig(cfg),
ec2.NewFromConfig(cfg),
ssm.NewFromConfig(cfg),
}, nil
}
func (p *Provider) loadConfiguration(ctx context.Context, client *awsClient, configurationChan chan<- dynamic.Message) error {
instances, err := p.listInstances(ctx, client)
if err != nil {
+76 -77
View File
@@ -5,6 +5,7 @@ import (
"context"
"errors"
"fmt"
"maps"
"os"
"os/signal"
"path"
@@ -112,6 +113,51 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
return nil
}
// CreateConfiguration creates a provider configuration from content using templating.
func (p *Provider) CreateConfiguration(ctx context.Context, filename string, funcMap template.FuncMap, templateObjects any) (*dynamic.Configuration, error) {
tmplContent, err := readFile(filename)
if err != nil {
return nil, fmt.Errorf("error reading configuration file: %s - %w", filename, err)
}
defaultFuncMap := sprig.TxtFuncMap()
defaultFuncMap["normalize"] = provider.Normalize
defaultFuncMap["split"] = strings.Split
maps.Copy(defaultFuncMap, funcMap)
tmpl := template.New(p.Filename).Funcs(defaultFuncMap)
_, err = tmpl.Parse(tmplContent)
if err != nil {
return nil, err
}
var buffer bytes.Buffer
err = tmpl.Execute(&buffer, templateObjects)
if err != nil {
return nil, err
}
renderedTemplate := buffer.String()
if p.DebugLogGeneratedTemplate {
logger := log.Ctx(ctx)
logger.Debug().Msgf("Template content: %s", tmplContent)
logger.Debug().Msgf("Rendering results: %s", renderedTemplate)
}
return p.decodeConfiguration(filename, renderedTemplate)
}
// DecodeConfiguration Decodes a *types.Configuration from a content.
func (p *Provider) DecodeConfiguration(filename string) (*dynamic.Configuration, error) {
content, err := readFile(filename)
if err != nil {
return nil, fmt.Errorf("error reading configuration file: %s - %w", filename, err)
}
return p.decodeConfiguration(filename, content)
}
func (p *Provider) addWatcher(pool *safe.Pool, items []string, configurationChan chan<- dynamic.Message, callback func(chan<- dynamic.Message) error) error {
watcher, err := fsnotify.NewWatcher()
if err != nil {
@@ -185,13 +231,6 @@ func (p *Provider) buildConfiguration() (*dynamic.Configuration, error) {
return nil, errors.New("error using file configuration provider, neither filename nor directory is defined")
}
func sendConfigToChannel(configurationChan chan<- dynamic.Message, configuration *dynamic.Configuration) {
configurationChan <- dynamic.Message{
ProviderName: "file",
Configuration: configuration,
}
}
func (p *Provider) loadFileConfig(ctx context.Context, filename string, parseTemplate bool) (*dynamic.Configuration, error) {
var err error
var configuration *dynamic.Configuration
@@ -337,29 +376,6 @@ func (p *Provider) loadFileConfig(ctx context.Context, filename string, parseTem
return configuration, nil
}
func flattenCertificates(ctx context.Context, tlsConfig *dynamic.TLSConfiguration) []*tls.CertAndStores {
var certs []*tls.CertAndStores
for _, cert := range tlsConfig.Certificates {
content, err := cert.Certificate.CertFile.Read()
if err != nil {
log.Ctx(ctx).Error().Err(err).Send()
continue
}
cert.Certificate.CertFile = types.FileOrContent(string(content))
content, err = cert.Certificate.KeyFile.Read()
if err != nil {
log.Ctx(ctx).Error().Err(err).Send()
continue
}
cert.Certificate.KeyFile = types.FileOrContent(string(content))
certs = append(certs, cert)
}
return certs
}
func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory string, configuration *dynamic.Configuration) (*dynamic.Configuration, error) {
fileList, err := os.ReadDir(directory)
if err != nil {
@@ -539,53 +555,6 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
return configuration, nil
}
// CreateConfiguration creates a provider configuration from content using templating.
func (p *Provider) CreateConfiguration(ctx context.Context, filename string, funcMap template.FuncMap, templateObjects interface{}) (*dynamic.Configuration, error) {
tmplContent, err := readFile(filename)
if err != nil {
return nil, fmt.Errorf("error reading configuration file: %s - %w", filename, err)
}
defaultFuncMap := sprig.TxtFuncMap()
defaultFuncMap["normalize"] = provider.Normalize
defaultFuncMap["split"] = strings.Split
for funcID, funcElement := range funcMap {
defaultFuncMap[funcID] = funcElement
}
tmpl := template.New(p.Filename).Funcs(defaultFuncMap)
_, err = tmpl.Parse(tmplContent)
if err != nil {
return nil, err
}
var buffer bytes.Buffer
err = tmpl.Execute(&buffer, templateObjects)
if err != nil {
return nil, err
}
renderedTemplate := buffer.String()
if p.DebugLogGeneratedTemplate {
logger := log.Ctx(ctx)
logger.Debug().Msgf("Template content: %s", tmplContent)
logger.Debug().Msgf("Rendering results: %s", renderedTemplate)
}
return p.decodeConfiguration(filename, renderedTemplate)
}
// DecodeConfiguration Decodes a *types.Configuration from a content.
func (p *Provider) DecodeConfiguration(filename string) (*dynamic.Configuration, error) {
content, err := readFile(filename)
if err != nil {
return nil, fmt.Errorf("error reading configuration file: %s - %w", filename, err)
}
return p.decodeConfiguration(filename, content)
}
func (p *Provider) decodeConfiguration(filePath, content string) (*dynamic.Configuration, error) {
configuration := &dynamic.Configuration{
HTTP: &dynamic.HTTPConfiguration{
@@ -618,6 +587,36 @@ func (p *Provider) decodeConfiguration(filePath, content string) (*dynamic.Confi
return configuration, nil
}
func sendConfigToChannel(configurationChan chan<- dynamic.Message, configuration *dynamic.Configuration) {
configurationChan <- dynamic.Message{
ProviderName: "file",
Configuration: configuration,
}
}
func flattenCertificates(ctx context.Context, tlsConfig *dynamic.TLSConfiguration) []*tls.CertAndStores {
var certs []*tls.CertAndStores
for _, cert := range tlsConfig.Certificates {
content, err := cert.Certificate.CertFile.Read()
if err != nil {
log.Ctx(ctx).Error().Err(err).Send()
continue
}
cert.Certificate.CertFile = types.FileOrContent(string(content))
content, err = cert.Certificate.KeyFile.Read()
if err != nil {
log.Ctx(ctx).Error().Err(err).Send()
continue
}
cert.Certificate.KeyFile = types.FileOrContent(string(content))
certs = append(certs, cert)
}
return certs
}
func readFile(filename string) (string, error) {
if len(filename) > 0 {
buf, err := os.ReadFile(filename)
+3 -3
View File
@@ -34,7 +34,7 @@ const resyncPeriod = 10 * time.Minute
// WatchAll starts the watch of the Provider resources and updates the stores.
// The stores can then be accessed via the Get* functions.
type Client interface {
WatchAll(namespaces []string, stopCh <-chan struct{}) (<-chan interface{}, error)
WatchAll(namespaces []string, stopCh <-chan struct{}) (<-chan any, error)
GetIngressRoutes() []*traefikv1alpha1.IngressRoute
GetIngressRouteTCPs() []*traefikv1alpha1.IngressRouteTCP
GetIngressRouteUDPs() []*traefikv1alpha1.IngressRouteUDP
@@ -157,8 +157,8 @@ func newExternalClusterClient(endpoint, caFilePath string, token types.FileOrCon
}
// WatchAll starts namespace-specific controllers for all relevant kinds.
func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<-chan interface{}, error) {
eventCh := make(chan interface{}, 1)
func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<-chan any, error) {
eventCh := make(chan any, 1)
eventHandler := &k8s.ResourceEventHandler{Ev: eventCh}
if len(namespaces) == 0 {
+2 -2
View File
@@ -29,8 +29,8 @@ func TestClientIgnoresHelmOwnedSecrets(t *testing.T) {
},
}
kubeClient := kubefake.NewSimpleClientset(helmSecret, secret)
crdClient := traefikcrdfake.NewSimpleClientset()
kubeClient := kubefake.NewClientset(helmSecret, secret)
crdClient := traefikcrdfake.NewClientset()
client := newClientImpl(kubeClient, crdClient)
@@ -0,0 +1,70 @@
/*
The MIT License (MIT)
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
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Code generated by applyconfiguration-gen. DO NOT EDIT.
package internal
import (
fmt "fmt"
sync "sync"
typed "sigs.k8s.io/structured-merge-diff/v6/typed"
)
func Parser() *typed.Parser {
parserOnce.Do(func() {
var err error
parser, err = typed.NewParser(schemaYAML)
if err != nil {
panic(fmt.Sprintf("Failed to parse schema: %v", err))
}
})
return parser
}
var parserOnce sync.Once
var parser *typed.Parser
var schemaYAML = typed.YAMLObject(`types:
- name: __untyped_atomic_
scalar: untyped
list:
elementType:
namedType: __untyped_atomic_
elementRelationship: atomic
map:
elementType:
namedType: __untyped_atomic_
elementRelationship: atomic
- name: __untyped_deduced_
scalar: untyped
list:
elementType:
namedType: __untyped_atomic_
elementRelationship: atomic
map:
elementType:
namedType: __untyped_deduced_
elementRelationship: separable
`)
@@ -0,0 +1,74 @@
/*
The MIT License (MIT)
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
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Code generated by applyconfiguration-gen. DO NOT EDIT.
package v1alpha1
// BasicAuthApplyConfiguration represents a declarative configuration of the BasicAuth type for use
// with apply.
type BasicAuthApplyConfiguration struct {
Secret *string `json:"secret,omitempty"`
Realm *string `json:"realm,omitempty"`
RemoveHeader *bool `json:"removeHeader,omitempty"`
HeaderField *string `json:"headerField,omitempty"`
}
// BasicAuthApplyConfiguration constructs a declarative configuration of the BasicAuth type for use with
// apply.
func BasicAuth() *BasicAuthApplyConfiguration {
return &BasicAuthApplyConfiguration{}
}
// WithSecret sets the Secret field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the Secret field is set to the value of the last call.
func (b *BasicAuthApplyConfiguration) WithSecret(value string) *BasicAuthApplyConfiguration {
b.Secret = &value
return b
}
// WithRealm sets the Realm field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the Realm field is set to the value of the last call.
func (b *BasicAuthApplyConfiguration) WithRealm(value string) *BasicAuthApplyConfiguration {
b.Realm = &value
return b
}
// WithRemoveHeader sets the RemoveHeader field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the RemoveHeader field is set to the value of the last call.
func (b *BasicAuthApplyConfiguration) WithRemoveHeader(value bool) *BasicAuthApplyConfiguration {
b.RemoveHeader = &value
return b
}
// WithHeaderField sets the HeaderField field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the HeaderField field is set to the value of the last call.
func (b *BasicAuthApplyConfiguration) WithHeaderField(value string) *BasicAuthApplyConfiguration {
b.HeaderField = &value
return b
}
@@ -0,0 +1,47 @@
/*
The MIT License (MIT)
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
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Code generated by applyconfiguration-gen. DO NOT EDIT.
package v1alpha1
// CertificateApplyConfiguration represents a declarative configuration of the Certificate type for use
// with apply.
type CertificateApplyConfiguration struct {
SecretName *string `json:"secretName,omitempty"`
}
// CertificateApplyConfiguration constructs a declarative configuration of the Certificate type for use with
// apply.
func Certificate() *CertificateApplyConfiguration {
return &CertificateApplyConfiguration{}
}
// WithSecretName sets the SecretName field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the SecretName field is set to the value of the last call.
func (b *CertificateApplyConfiguration) WithSecretName(value string) *CertificateApplyConfiguration {
b.SecretName = &value
return b
}

Some files were not shown because too many files have changed in this diff Show More