Merge branch v3.0 into master
This commit is contained in:
commit
1ffbffb26a
326 changed files with 8850 additions and 9322 deletions
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
@ -59,13 +60,9 @@ func (c *searchCriterion) searchIn(values ...string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
for _, v := range values {
|
||||
if strings.Contains(strings.ToLower(v), strings.ToLower(c.Search)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return slices.ContainsFunc(values, func(v string) bool {
|
||||
return strings.Contains(strings.ToLower(v), strings.ToLower(c.Search))
|
||||
})
|
||||
}
|
||||
|
||||
func (c *searchCriterion) filterService(name string) bool {
|
||||
|
|
|
@ -39,7 +39,7 @@ func Append(router *mux.Router, customAssets fs.FS) {
|
|||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
|
||||
w.Header().Del("Content-Type")
|
||||
|
||||
http.StripPrefix("/dashboard/", http.FileServer(http.FS(assets))).ServeHTTP(w, r)
|
||||
http.StripPrefix("/dashboard/", http.FileServerFS(assets)).ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ func (g Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
|
||||
w.Header().Del("Content-Type")
|
||||
|
||||
http.FileServer(http.FS(assets)).ServeHTTP(w, r)
|
||||
http.FileServerFS(assets).ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func safePrefix(req *http.Request) string {
|
||||
|
|
|
@ -42,7 +42,6 @@ func Test_safePrefix(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -91,7 +90,6 @@ func Test_ContentSecurityPolicy(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -149,7 +149,7 @@ func getProviderName(id string) string {
|
|||
|
||||
func extractType(element interface{}) string {
|
||||
v := reflect.ValueOf(element).Elem()
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
for i := range v.NumField() {
|
||||
field := v.Field(i)
|
||||
|
||||
if field.Kind() == reflect.Map && field.Type().Elem() == reflect.TypeOf(dynamic.PluginConf{}) {
|
||||
|
|
|
@ -210,7 +210,6 @@ func TestHandler_EntryPoints(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -256,7 +255,7 @@ func TestHandler_EntryPoints(t *testing.T) {
|
|||
|
||||
func generateEntryPoints(nb int) map[string]*static.EntryPoint {
|
||||
eps := make(map[string]*static.EntryPoint, nb)
|
||||
for i := 0; i < nb; i++ {
|
||||
for i := range nb {
|
||||
eps[fmt.Sprintf("ep%2d", i)] = &static.EntryPoint{
|
||||
Address: ":" + strconv.Itoa(i),
|
||||
}
|
||||
|
|
|
@ -998,7 +998,6 @@ func TestHandler_HTTP(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -1050,7 +1049,7 @@ func TestHandler_HTTP(t *testing.T) {
|
|||
|
||||
func generateHTTPRouters(nbRouters int) map[string]*runtime.RouterInfo {
|
||||
routers := make(map[string]*runtime.RouterInfo, nbRouters)
|
||||
for i := 0; i < nbRouters; i++ {
|
||||
for i := range nbRouters {
|
||||
routers[fmt.Sprintf("bar%2d@myprovider", i)] = &runtime.RouterInfo{
|
||||
Router: &dynamic.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
|
|
|
@ -226,7 +226,7 @@ func getProviders(conf static.Configuration) []string {
|
|||
var providers []string
|
||||
|
||||
v := reflect.ValueOf(conf.Providers).Elem()
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
for i := range v.NumField() {
|
||||
field := v.Field(i)
|
||||
if field.Kind() == reflect.Ptr && field.Elem().Kind() == reflect.Struct {
|
||||
if !field.IsNil() {
|
||||
|
@ -256,7 +256,7 @@ func getMetrics(conf static.Configuration) string {
|
|||
}
|
||||
|
||||
v := reflect.ValueOf(conf.Metrics).Elem()
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
for i := range v.NumField() {
|
||||
field := v.Field(i)
|
||||
if field.Kind() == reflect.Ptr && field.Elem().Kind() == reflect.Struct {
|
||||
if !field.IsNil() {
|
||||
|
@ -274,7 +274,7 @@ func getTracing(conf static.Configuration) string {
|
|||
}
|
||||
|
||||
v := reflect.ValueOf(conf.Tracing).Elem()
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
for i := range v.NumField() {
|
||||
field := v.Field(i)
|
||||
if field.Kind() == reflect.Ptr && field.Elem().Kind() == reflect.Struct {
|
||||
if !field.IsNil() {
|
||||
|
|
|
@ -269,7 +269,6 @@ func TestHandler_Overview(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -874,7 +874,6 @@ func TestHandler_TCP(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -127,7 +127,6 @@ func TestHandler_RawData(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -252,7 +251,6 @@ func TestHandler_GetMiddleware(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -564,7 +564,6 @@ func TestHandler_UDP(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -828,7 +828,6 @@ func TestSortRouters(t *testing.T) {
|
|||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(fmt.Sprintf("%s-%s", test.direction, test.sortBy), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -1339,7 +1338,6 @@ func TestSortServices(t *testing.T) {
|
|||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(fmt.Sprintf("%s-%s", test.direction, test.sortBy), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -1674,7 +1672,6 @@ func TestSortMiddlewares(t *testing.T) {
|
|||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(fmt.Sprintf("%s-%s", test.direction, test.sortBy), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -267,7 +267,6 @@ func TestDeprecationNotice(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -385,7 +384,6 @@ func TestLoad(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
tconfig := cmd.NewTraefikConfiguration()
|
||||
c := &cli.Command{Configuration: tconfig}
|
||||
|
|
|
@ -65,7 +65,7 @@ func createBody(staticConfiguration *static.Configuration) (*bytes.Buffer, error
|
|||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err = json.NewEncoder(buf).Encode(data) //nolint:musttag // cannot be changed for historical reasons.
|
||||
err = json.NewEncoder(buf).Encode(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ func fill(field reflect.Value) error {
|
|||
case reflect.Int64:
|
||||
switch field.Type() {
|
||||
case reflect.TypeOf(types.Duration(time.Second)):
|
||||
setTyped(field, int64(defaultNumber*int(time.Second)))
|
||||
setTyped(field, types.Duration(defaultNumber*time.Second))
|
||||
default:
|
||||
setTyped(field, int64(defaultNumber))
|
||||
}
|
||||
|
@ -93,12 +93,12 @@ func setTyped(field reflect.Value, i interface{}) {
|
|||
func setMap(field reflect.Value) error {
|
||||
field.Set(reflect.MakeMap(field.Type()))
|
||||
|
||||
for i := 0; i < mapItemNumber; i++ {
|
||||
for i := range mapItemNumber {
|
||||
baseKeyName := makeKeyName(field.Type().Elem())
|
||||
key := reflect.ValueOf(fmt.Sprintf("%s%d", baseKeyName, i))
|
||||
|
||||
// generate value
|
||||
ptrType := reflect.PtrTo(field.Type().Elem())
|
||||
ptrType := reflect.PointerTo(field.Type().Elem())
|
||||
ptrValue := reflect.New(ptrType)
|
||||
if err := fill(ptrValue); err != nil {
|
||||
return err
|
||||
|
@ -125,7 +125,7 @@ func makeKeyName(typ reflect.Type) string {
|
|||
}
|
||||
|
||||
func setStruct(field reflect.Value) error {
|
||||
for i := 0; i < field.NumField(); i++ {
|
||||
for i := range field.NumField() {
|
||||
fld := field.Field(i)
|
||||
stFld := field.Type().Field(i)
|
||||
|
||||
|
@ -142,7 +142,7 @@ func setStruct(field reflect.Value) error {
|
|||
|
||||
func setSlice(field reflect.Value) error {
|
||||
field.Set(reflect.MakeSlice(field.Type(), sliceItemNumber, sliceItemNumber))
|
||||
for j := 0; j < field.Len(); j++ {
|
||||
for j := range field.Len() {
|
||||
if err := fill(field.Index(j)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ type Configuration struct {
|
|||
HTTP *HTTPConfiguration `json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" export:"true"`
|
||||
TCP *TCPConfiguration `json:"tcp,omitempty" toml:"tcp,omitempty" yaml:"tcp,omitempty" export:"true"`
|
||||
UDP *UDPConfiguration `json:"udp,omitempty" toml:"udp,omitempty" yaml:"udp,omitempty" export:"true"`
|
||||
TLS *TLSConfiguration `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"-" export:"true"`
|
||||
TLS *TLSConfiguration `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
@ -32,6 +32,6 @@ type Configuration struct {
|
|||
// TLSConfiguration contains all the configuration parameters of a TLS connection.
|
||||
type TLSConfiguration struct {
|
||||
Certificates []*tls.CertAndStores `json:"certificates,omitempty" toml:"certificates,omitempty" yaml:"certificates,omitempty" label:"-" export:"true"`
|
||||
Options map[string]tls.Options `json:"options,omitempty" toml:"options,omitempty" yaml:"options,omitempty" export:"true"`
|
||||
Options map[string]tls.Options `json:"options,omitempty" toml:"options,omitempty" yaml:"options,omitempty" label:"-" export:"true"`
|
||||
Stores map[string]tls.Store `json:"stores,omitempty" toml:"stores,omitempty" yaml:"stores,omitempty" export:"true"`
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ func getFieldNames(rootName string, rootType reflect.Type) []string {
|
|||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < rootType.NumField(); i++ {
|
||||
for i := range rootType.NumField() {
|
||||
field := rootType.Field(i)
|
||||
|
||||
if !parser.IsExported(field) {
|
||||
|
|
|
@ -244,7 +244,6 @@ func TestDecodeToNode(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -87,7 +87,6 @@ func TestDecode(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -12,9 +12,11 @@ func DecodeConfiguration(labels map[string]string) (*dynamic.Configuration, erro
|
|||
HTTP: &dynamic.HTTPConfiguration{},
|
||||
TCP: &dynamic.TCPConfiguration{},
|
||||
UDP: &dynamic.UDPConfiguration{},
|
||||
TLS: &dynamic.TLSConfiguration{},
|
||||
}
|
||||
|
||||
err := parser.Decode(labels, conf, parser.DefaultRootName, "traefik.http", "traefik.tcp", "traefik.udp")
|
||||
// When decoding the TLS configuration we are making sure that only the default TLS store can be configured.
|
||||
err := parser.Decode(labels, conf, parser.DefaultRootName, "traefik.http", "traefik.tcp", "traefik.udp", "traefik.tls.stores.default")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
ptypes "github.com/traefik/paerser/types"
|
||||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v3/pkg/tls"
|
||||
"github.com/traefik/traefik/v3/pkg/types"
|
||||
)
|
||||
|
||||
func Bool(v bool) *bool { return &v }
|
||||
|
@ -217,6 +219,10 @@ func TestDecodeConfiguration(t *testing.T) {
|
|||
"traefik.udp.routers.Router1.service": "foobar",
|
||||
"traefik.udp.services.Service0.loadbalancer.server.Port": "42",
|
||||
"traefik.udp.services.Service1.loadbalancer.server.Port": "42",
|
||||
|
||||
"traefik.tls.stores.default.defaultgeneratedcert.resolver": "foobar",
|
||||
"traefik.tls.stores.default.defaultgeneratedcert.domain.main": "foobar",
|
||||
"traefik.tls.stores.default.defaultgeneratedcert.domain.sans": "foobar, fiibar",
|
||||
}
|
||||
|
||||
configuration, err := DecodeConfiguration(labels)
|
||||
|
@ -719,6 +725,19 @@ func TestDecodeConfiguration(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
TLS: &dynamic.TLSConfiguration{
|
||||
Stores: map[string]tls.Store{
|
||||
"default": {
|
||||
DefaultGeneratedCert: &tls.GeneratedCert{
|
||||
Resolver: "foobar",
|
||||
Domain: &types.Domain{
|
||||
Main: "foobar",
|
||||
SANs: []string{"foobar", "fiibar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.Nil(t, configuration.HTTP.ServersTransports)
|
||||
|
@ -1216,6 +1235,19 @@ func TestEncodeConfiguration(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
TLS: &dynamic.TLSConfiguration{
|
||||
Stores: map[string]tls.Store{
|
||||
"default": {
|
||||
DefaultGeneratedCert: &tls.GeneratedCert{
|
||||
Resolver: "foobar",
|
||||
Domain: &types.Domain{
|
||||
Main: "foobar",
|
||||
SANs: []string{"foobar", "fiibar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
labels, err := EncodeConfiguration(configuration)
|
||||
|
@ -1415,6 +1447,10 @@ func TestEncodeConfiguration(t *testing.T) {
|
|||
"traefik.TCP.Services.Service1.LoadBalancer.ServersTransport": "foo",
|
||||
"traefik.TCP.Services.Service1.LoadBalancer.TerminationDelay": "42",
|
||||
|
||||
"traefik.TLS.Stores.default.DefaultGeneratedCert.Resolver": "foobar",
|
||||
"traefik.TLS.Stores.default.DefaultGeneratedCert.Domain.Main": "foobar",
|
||||
"traefik.TLS.Stores.default.DefaultGeneratedCert.Domain.SANs": "foobar, fiibar",
|
||||
|
||||
"traefik.UDP.Routers.Router0.EntryPoints": "foobar, fiibar",
|
||||
"traefik.UDP.Routers.Router0.Service": "foobar",
|
||||
"traefik.UDP.Routers.Router1.EntryPoints": "foobar, fiibar",
|
||||
|
|
|
@ -208,7 +208,6 @@ func TestGetRoutersByEntryPoints(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runtimeConfig := NewConfig(test.conf)
|
||||
|
|
|
@ -208,7 +208,6 @@ func TestGetTCPRoutersByEntryPoints(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runtimeConfig := NewConfig(test.conf)
|
||||
|
|
|
@ -665,8 +665,6 @@ func TestPopulateUsedBy(t *testing.T) {
|
|||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -189,7 +189,6 @@ func TestGetUDPRoutersByEntryPoints(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runtimeConfig := NewConfig(test.conf)
|
||||
|
|
|
@ -97,7 +97,7 @@ type RedirectEntryPoint struct {
|
|||
func (r *RedirectEntryPoint) SetDefaults() {
|
||||
r.Scheme = "https"
|
||||
r.Permanent = true
|
||||
r.Priority = math.MaxInt32 - 1
|
||||
r.Priority = math.MaxInt - 1
|
||||
}
|
||||
|
||||
// TLSConfig is the default TLS configuration for all the routers associated to the concerned entry point.
|
||||
|
|
|
@ -193,10 +193,12 @@ func (a *LifeCycle) SetDefaults() {
|
|||
|
||||
// Tracing holds the tracing configuration.
|
||||
type Tracing struct {
|
||||
ServiceName string `description:"Set the name for this service." json:"serviceName,omitempty" toml:"serviceName,omitempty" yaml:"serviceName,omitempty" export:"true"`
|
||||
GlobalAttributes map[string]string `description:"Defines additional attributes (key:value) on all spans." json:"globalAttributes,omitempty" toml:"globalAttributes,omitempty" yaml:"globalAttributes,omitempty" export:"true"`
|
||||
SampleRate float64 `description:"Sets the rate between 0.0 and 1.0 of requests to trace." json:"sampleRate,omitempty" toml:"sampleRate,omitempty" yaml:"sampleRate,omitempty" export:"true"`
|
||||
AddInternals bool `description:"Enables tracing for internal services (ping, dashboard, etc...)." json:"addInternals,omitempty" toml:"addInternals,omitempty" yaml:"addInternals,omitempty" export:"true"`
|
||||
ServiceName string `description:"Set the name for this service." json:"serviceName,omitempty" toml:"serviceName,omitempty" yaml:"serviceName,omitempty" export:"true"`
|
||||
GlobalAttributes map[string]string `description:"Defines additional attributes (key:value) on all spans." json:"globalAttributes,omitempty" toml:"globalAttributes,omitempty" yaml:"globalAttributes,omitempty" export:"true"`
|
||||
CapturedRequestHeaders []string `description:"Request headers to add as attributes for server and client spans." json:"capturedRequestHeaders,omitempty" toml:"capturedRequestHeaders,omitempty" yaml:"capturedRequestHeaders,omitempty" export:"true"`
|
||||
CapturedResponseHeaders []string `description:"Response headers to add as attributes for server and client spans." json:"capturedResponseHeaders,omitempty" toml:"capturedResponseHeaders,omitempty" yaml:"capturedResponseHeaders,omitempty" export:"true"`
|
||||
SampleRate float64 `description:"Sets the rate between 0.0 and 1.0 of requests to trace." json:"sampleRate,omitempty" toml:"sampleRate,omitempty" yaml:"sampleRate,omitempty" export:"true"`
|
||||
AddInternals bool `description:"Enables tracing for internal services (ping, dashboard, etc...)." json:"addInternals,omitempty" toml:"addInternals,omitempty" yaml:"addInternals,omitempty" export:"true"`
|
||||
|
||||
OTLP *opentelemetry.Config `description:"Settings for OpenTelemetry." json:"otlp,omitempty" toml:"otlp,omitempty" yaml:"otlp,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
}
|
||||
|
@ -286,6 +288,10 @@ func (c *Configuration) SetEffectiveConfiguration() {
|
|||
entryPoints[epName] = gateway.Entrypoint{Address: entryPoint.GetAddress(), HasHTTPTLSConf: entryPoint.HTTP.TLS != nil}
|
||||
}
|
||||
|
||||
if c.Providers.KubernetesCRD != nil {
|
||||
c.Providers.KubernetesCRD.FillExtensionBuilderRegistry(c.Providers.KubernetesGateway)
|
||||
}
|
||||
|
||||
c.Providers.KubernetesGateway.EntryPoints = entryPoints
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ func TestHasEntrypoint(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -243,7 +243,6 @@ func TestServiceHealthChecker_newRequest(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -405,7 +404,6 @@ func TestServiceHealthChecker_Launch(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -36,7 +36,6 @@ func TestIsAuthorized(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -117,7 +116,6 @@ func TestNew(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -289,7 +287,6 @@ func TestContainsIsAllowed(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ func TestRemoteAddrStrategy_GetIP(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -61,7 +60,6 @@ func TestDepthStrategy_GetIP(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -109,7 +107,6 @@ func TestTrustedIPsStrategy_GetIP(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -67,7 +67,6 @@ func TestDatadog_parseDatadogAddress(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -30,6 +30,74 @@ var (
|
|||
openTelemetryGaugeCollector *gaugeCollector
|
||||
)
|
||||
|
||||
// SetMeterProvider sets the meter provider for the tests.
|
||||
func SetMeterProvider(meterProvider *sdkmetric.MeterProvider) {
|
||||
openTelemetryMeterProvider = meterProvider
|
||||
otel.SetMeterProvider(meterProvider)
|
||||
}
|
||||
|
||||
// SemConvMetricsRegistry holds stables semantic conventions metric instruments.
|
||||
type SemConvMetricsRegistry struct {
|
||||
// server metrics
|
||||
httpServerRequestDuration metric.Float64Histogram
|
||||
// client metrics
|
||||
httpClientRequestDuration metric.Float64Histogram
|
||||
}
|
||||
|
||||
// NewSemConvMetricRegistry registers all stables semantic conventions metrics.
|
||||
func NewSemConvMetricRegistry(ctx context.Context, config *types.OTLP) (*SemConvMetricsRegistry, error) {
|
||||
if openTelemetryMeterProvider == nil {
|
||||
var err error
|
||||
if openTelemetryMeterProvider, err = newOpenTelemetryMeterProvider(ctx, config); err != nil {
|
||||
log.Ctx(ctx).Err(err).Msg("Unable to create OpenTelemetry meter provider")
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
meter := otel.Meter("github.com/traefik/traefik",
|
||||
metric.WithInstrumentationVersion(version.Version))
|
||||
|
||||
httpServerRequestDuration, err := meter.Float64Histogram("http.server.request.duration",
|
||||
metric.WithDescription("Duration of HTTP server requests."),
|
||||
metric.WithUnit("s"),
|
||||
metric.WithExplicitBucketBoundaries(config.ExplicitBoundaries...))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't build httpServerRequestDuration histogram: %w", err)
|
||||
}
|
||||
|
||||
httpClientRequestDuration, err := meter.Float64Histogram("http.client.request.duration",
|
||||
metric.WithDescription("Duration of HTTP client requests."),
|
||||
metric.WithUnit("s"),
|
||||
metric.WithExplicitBucketBoundaries(config.ExplicitBoundaries...))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't build httpClientRequestDuration histogram: %w", err)
|
||||
}
|
||||
|
||||
return &SemConvMetricsRegistry{
|
||||
httpServerRequestDuration: httpServerRequestDuration,
|
||||
httpClientRequestDuration: httpClientRequestDuration,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// HTTPServerRequestDuration returns the HTTP server request duration histogram.
|
||||
func (s *SemConvMetricsRegistry) HTTPServerRequestDuration() metric.Float64Histogram {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return s.httpServerRequestDuration
|
||||
}
|
||||
|
||||
// HTTPClientRequestDuration returns the HTTP client request duration histogram.
|
||||
func (s *SemConvMetricsRegistry) HTTPClientRequestDuration() metric.Float64Histogram {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return s.httpClientRequestDuration
|
||||
}
|
||||
|
||||
// RegisterOpenTelemetry registers all OpenTelemetry metrics.
|
||||
func RegisterOpenTelemetry(ctx context.Context, config *types.OTLP) Registry {
|
||||
if openTelemetryMeterProvider == nil {
|
||||
|
|
|
@ -89,8 +89,6 @@ func TestOpenTelemetry_labels(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -181,8 +179,6 @@ func TestOpenTelemetry_GaugeCollectorAdd(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -275,8 +271,6 @@ func TestOpenTelemetry_GaugeCollectorSet(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -361,7 +355,7 @@ func TestOpenTelemetry(t *testing.T) {
|
|||
expectedEntryPoints := []string{
|
||||
`({"name":"traefik_entrypoint_requests_total","description":"How many HTTP requests processed on an entrypoint, partitioned by status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"200"}},{"key":"entrypoint","value":{"stringValue":"test1"}},{"key":"method","value":{"stringValue":"GET"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
|
||||
`({"name":"traefik_entrypoint_requests_tls_total","description":"How many HTTP requests with TLS processed on an entrypoint, partitioned by TLS Version and TLS cipher Used.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"entrypoint","value":{"stringValue":"test2"}},{"key":"tls_cipher","value":{"stringValue":"bar"}},{"key":"tls_version","value":{"stringValue":"foo"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
|
||||
`({"name":"traefik_entrypoint_request_duration_seconds","description":"How long it took to process the request on an entrypoint, partitioned by status code, protocol, and method.","unit":"ms","histogram":{"dataPoints":\[{"attributes":\[{"key":"entrypoint","value":{"stringValue":"test3"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","count":"1","sum":10000,"bucketCounts":\["0","0","0","0","0","0","0","0","0","0","0","1"\],"explicitBounds":\[0.005,0.01,0.025,0.05,0.1,0.25,0.5,1,2.5,5,10\],"min":10000,"max":10000}\],"aggregationTemporality":2}})`,
|
||||
`({"name":"traefik_entrypoint_request_duration_seconds","description":"How long it took to process the request on an entrypoint, partitioned by status code, protocol, and method.","unit":"ms","histogram":{"dataPoints":\[{"attributes":\[{"key":"entrypoint","value":{"stringValue":"test3"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","count":"1","sum":10000,"bucketCounts":\["0","0","0","0","0","0","0","0","0","0","0","0","0","0","1"\],"explicitBounds":\[0.005,0.01,0.025,0.05,0.075,0.1,0.25,0.5,0.75,1,2.5,5,7.5,10\],"min":10000,"max":10000}\],"aggregationTemporality":2}})`,
|
||||
`({"name":"traefik_entrypoint_requests_bytes_total","description":"The total size of requests in bytes handled by an entrypoint, partitioned by status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"200"}},{"key":"entrypoint","value":{"stringValue":"test1"}},{"key":"method","value":{"stringValue":"GET"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
|
||||
`({"name":"traefik_entrypoint_responses_bytes_total","description":"The total size of responses in bytes handled by an entrypoint, partitioned by status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"200"}},{"key":"entrypoint","value":{"stringValue":"test1"}},{"key":"method","value":{"stringValue":"GET"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
|
||||
}
|
||||
|
@ -377,7 +371,7 @@ func TestOpenTelemetry(t *testing.T) {
|
|||
expectedRouters := []string{
|
||||
`({"name":"traefik_router_requests_total","description":"How many HTTP requests are processed on a router, partitioned by service, status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"(?:200|404)"}},{"key":"method","value":{"stringValue":"GET"}},{"key":"router","value":{"stringValue":"RouterReqsCounter"}},{"key":"service","value":{"stringValue":"test"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1},{"attributes":\[{"key":"code","value":{"stringValue":"(?:200|404)"}},{"key":"method","value":{"stringValue":"GET"}},{"key":"router","value":{"stringValue":"RouterReqsCounter"}},{"key":"service","value":{"stringValue":"test"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
|
||||
`({"name":"traefik_router_requests_tls_total","description":"How many HTTP requests with TLS are processed on a router, partitioned by service, TLS Version, and TLS cipher Used.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"router","value":{"stringValue":"demo"}},{"key":"service","value":{"stringValue":"test"}},{"key":"tls_cipher","value":{"stringValue":"bar"}},{"key":"tls_version","value":{"stringValue":"foo"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
|
||||
`({"name":"traefik_router_request_duration_seconds","description":"How long it took to process the request on a router, partitioned by service, status code, protocol, and method.","unit":"ms","histogram":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"200"}},{"key":"router","value":{"stringValue":"demo"}},{"key":"service","value":{"stringValue":"test"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","count":"1","sum":10000,"bucketCounts":\["0","0","0","0","0","0","0","0","0","0","0","1"\],"explicitBounds":\[0.005,0.01,0.025,0.05,0.1,0.25,0.5,1,2.5,5,10\],"min":10000,"max":10000}\],"aggregationTemporality":2}})`,
|
||||
`({"name":"traefik_router_request_duration_seconds","description":"How long it took to process the request on a router, partitioned by service, status code, protocol, and method.","unit":"ms","histogram":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"200"}},{"key":"router","value":{"stringValue":"demo"}},{"key":"service","value":{"stringValue":"test"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","count":"1","sum":10000,"bucketCounts":\["0","0","0","0","0","0","0","0","0","0","0","0","0","0","1"\],"explicitBounds":\[0.005,0.01,0.025,0.05,0.075,0.1,0.25,0.5,0.75,1,2.5,5,7.5,10\],"min":10000,"max":10000}\],"aggregationTemporality":2}})`,
|
||||
`({"name":"traefik_router_requests_bytes_total","description":"The total size of requests in bytes handled by a router, partitioned by status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"404"}},{"key":"method","value":{"stringValue":"GET"}},{"key":"router","value":{"stringValue":"RouterReqsCounter"}},{"key":"service","value":{"stringValue":"test"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
|
||||
`({"name":"traefik_router_responses_bytes_total","description":"The total size of responses in bytes handled by a router, partitioned by status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"404"}},{"key":"method","value":{"stringValue":"GET"}},{"key":"router","value":{"stringValue":"RouterReqsCounter"}},{"key":"service","value":{"stringValue":"test"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
|
||||
}
|
||||
|
@ -394,7 +388,7 @@ func TestOpenTelemetry(t *testing.T) {
|
|||
expectedServices := []string{
|
||||
`({"name":"traefik_service_requests_total","description":"How many HTTP requests processed on a service, partitioned by status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"(?:200|404)"}},{"key":"method","value":{"stringValue":"GET"}},{"key":"service","value":{"stringValue":"ServiceReqsCounter"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1},{"attributes":\[{"key":"code","value":{"stringValue":"(?:200|404)"}},{"key":"method","value":{"stringValue":"GET"}},{"key":"service","value":{"stringValue":"ServiceReqsCounter"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
|
||||
`({"name":"traefik_service_requests_tls_total","description":"How many HTTP requests with TLS processed on a service, partitioned by TLS version and TLS cipher.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"service","value":{"stringValue":"test"}},{"key":"tls_cipher","value":{"stringValue":"bar"}},{"key":"tls_version","value":{"stringValue":"foo"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
|
||||
`({"name":"traefik_service_request_duration_seconds","description":"How long it took to process the request on a service, partitioned by status code, protocol, and method.","unit":"ms","histogram":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"200"}},{"key":"service","value":{"stringValue":"test"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","count":"1","sum":10000,"bucketCounts":\["0","0","0","0","0","0","0","0","0","0","0","1"\],"explicitBounds":\[0.005,0.01,0.025,0.05,0.1,0.25,0.5,1,2.5,5,10\],"min":10000,"max":10000}\],"aggregationTemporality":2}})`,
|
||||
`({"name":"traefik_service_request_duration_seconds","description":"How long it took to process the request on a service, partitioned by status code, protocol, and method.","unit":"ms","histogram":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"200"}},{"key":"service","value":{"stringValue":"test"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","count":"1","sum":10000,"bucketCounts":\["0","0","0","0","0","0","0","0","0","0","0","0","0","0","1"\],"explicitBounds":\[0.005,0.01,0.025,0.05,0.075,0.1,0.25,0.5,0.75,1,2.5,5,7.5,10\],"min":10000,"max":10000}\],"aggregationTemporality":2}})`,
|
||||
`({"name":"traefik_service_server_up","description":"service server is up, described by gauge value of 0 or 1.","unit":"1","gauge":{"dataPoints":\[{"attributes":\[{"key":"service","value":{"stringValue":"test"}},{"key":"url","value":{"stringValue":"http://127.0.0.1"}}\],"timeUnixNano":"[\d]{19}","asDouble":1}\]}})`,
|
||||
`({"name":"traefik_service_requests_bytes_total","description":"The total size of requests in bytes received by a service, partitioned by status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"404"}},{"key":"method","value":{"stringValue":"GET"}},{"key":"service","value":{"stringValue":"ServiceReqsCounter"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
|
||||
`({"name":"traefik_service_responses_bytes_total","description":"The total size of responses in bytes returned by a service, partitioned by status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"404"}},{"key":"method","value":{"stringValue":"GET"}},{"key":"service","value":{"stringValue":"ServiceReqsCounter"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
|
||||
|
@ -426,7 +420,7 @@ func TestOpenTelemetry(t *testing.T) {
|
|||
// and as soon as the EntryPointReqDurationHistogram.Observe is called,
|
||||
// it adds a new dataPoint to the histogram.
|
||||
expectedEntryPointReqDuration := []string{
|
||||
`({"attributes":\[{"key":"entrypoint","value":{"stringValue":"myEntrypoint"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","count":"2","sum":30000,"bucketCounts":\["0","0","0","0","0","0","0","0","0","0","0","2"\],"explicitBounds":\[0.005,0.01,0.025,0.05,0.1,0.25,0.5,1,2.5,5,10\],"min":10000,"max":20000})`,
|
||||
`({"attributes":\[{"key":"entrypoint","value":{"stringValue":"myEntrypoint"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","count":"2","sum":30000,"bucketCounts":\["0","0","0","0","0","0","0","0","0","0","0","0","0","0","2"\],"explicitBounds":\[0.005,0.01,0.025,0.05,0.075,0.1,0.25,0.5,0.75,1,2.5,5,7.5,10\],"min":10000,"max":20000})`,
|
||||
}
|
||||
|
||||
registry.EntryPointReqDurationHistogram().With("entrypoint", "myEntrypoint").Observe(10000)
|
||||
|
|
|
@ -64,7 +64,6 @@ func TestRegisterPromState(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
actualNbRegistries := 0
|
||||
for _, prom := range test.prometheusSlice {
|
||||
|
@ -381,7 +380,6 @@ func TestPrometheus(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
family := findMetricFamily(test.name, metricsFamilies)
|
||||
if family == nil {
|
||||
|
|
|
@ -86,7 +86,6 @@ func TestCommonLogFormatter_Format(t *testing.T) {
|
|||
t.Setenv("TZ", "Etc/GMT+9")
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -150,7 +149,6 @@ func Test_toLog(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ func TestLogRotation(t *testing.T) {
|
|||
halfDone := make(chan bool)
|
||||
writeDone := make(chan bool)
|
||||
go func() {
|
||||
for i := 0; i < iterations; i++ {
|
||||
for i := range iterations {
|
||||
handler.ServeHTTP(recorder, req)
|
||||
if i == iterations/2 {
|
||||
halfDone <- true
|
||||
|
@ -180,7 +180,6 @@ func TestLoggerHeaderFields(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
logFile, err := os.CreateTemp(t.TempDir(), "*.log")
|
||||
require.NoError(t, err)
|
||||
|
@ -469,7 +468,6 @@ func TestLoggerJSON(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -687,7 +685,6 @@ func TestNewLogHandlerOutputStdout(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
// NOTE: It is not possible to run these cases in parallel because we capture Stdout
|
||||
|
||||
|
|
|
@ -60,7 +60,6 @@ func TestParseAccessLog(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -28,8 +28,6 @@ func TestSaveRetries(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
|
||||
t.Run(fmt.Sprintf("%d retries", test.requestAttempt), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
saveRetries := &SaveRetries{}
|
||||
|
|
|
@ -29,7 +29,6 @@ func TestNewAddPrefix(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -75,7 +74,6 @@ func TestAddPrefix(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/accesslog"
|
||||
"github.com/traefik/traefik/v3/pkg/tracing"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/observability"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
|
@ -77,7 +77,7 @@ func (b *basicAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||
|
||||
if !ok {
|
||||
logger.Debug().Msg("Authentication failed")
|
||||
tracing.SetStatusErrorf(req.Context(), "Authentication failed")
|
||||
observability.SetStatusErrorf(req.Context(), "Authentication failed")
|
||||
|
||||
b.auth.RequireAuth(rw, req)
|
||||
return
|
||||
|
|
|
@ -210,7 +210,6 @@ func TestBasicAuthUsersFromFile(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/accesslog"
|
||||
"github.com/traefik/traefik/v3/pkg/tracing"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/observability"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
|
@ -78,13 +78,13 @@ func (d *digestAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||
|
||||
if authinfo != nil && *authinfo == "stale" {
|
||||
logger.Debug().Msg("Digest authentication failed, possibly because out of order requests")
|
||||
tracing.SetStatusErrorf(req.Context(), "Digest authentication failed, possibly because out of order requests")
|
||||
observability.SetStatusErrorf(req.Context(), "Digest authentication failed, possibly because out of order requests")
|
||||
d.auth.RequireAuthStale(rw, req)
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debug().Msg("Digest authentication failed")
|
||||
tracing.SetStatusErrorf(req.Context(), "Digest authentication failed")
|
||||
observability.SetStatusErrorf(req.Context(), "Digest authentication failed")
|
||||
d.auth.RequireAuth(rw, req)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -88,7 +88,6 @@ func TestDigestAuthUsersFromFile(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/connectionheader"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/observability"
|
||||
"github.com/traefik/traefik/v3/pkg/tracing"
|
||||
"github.com/traefik/traefik/v3/pkg/types"
|
||||
"github.com/vulcand/oxy/v2/forward"
|
||||
|
@ -126,13 +127,16 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||
if err != nil {
|
||||
logMessage := fmt.Sprintf("Error calling %s. Cause %s", fa.address, err)
|
||||
logger.Debug().Msg(logMessage)
|
||||
tracing.SetStatusErrorf(req.Context(), logMessage)
|
||||
observability.SetStatusErrorf(req.Context(), logMessage)
|
||||
rw.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
writeHeader(req, forwardReq, fa.trustForwardHeader, fa.authRequestHeaders)
|
||||
|
||||
var forwardSpan trace.Span
|
||||
if tracer := tracing.TracerFromContext(req.Context()); tracer != nil {
|
||||
var tracer *tracing.Tracer
|
||||
if tracer = tracing.TracerFromContext(req.Context()); tracer != nil {
|
||||
var tracingCtx context.Context
|
||||
tracingCtx, forwardSpan = tracer.Start(req.Context(), "AuthRequest", trace.WithSpanKind(trace.SpanKindClient))
|
||||
defer forwardSpan.End()
|
||||
|
@ -140,16 +144,14 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||
forwardReq = forwardReq.WithContext(tracingCtx)
|
||||
|
||||
tracing.InjectContextIntoCarrier(forwardReq)
|
||||
tracing.LogClientRequest(forwardSpan, forwardReq)
|
||||
tracer.CaptureClientRequest(forwardSpan, forwardReq)
|
||||
}
|
||||
|
||||
writeHeader(req, forwardReq, fa.trustForwardHeader, fa.authRequestHeaders)
|
||||
|
||||
forwardResponse, forwardErr := fa.client.Do(forwardReq)
|
||||
if forwardErr != nil {
|
||||
logMessage := fmt.Sprintf("Error calling %s. Cause: %s", fa.address, forwardErr)
|
||||
logger.Debug().Msg(logMessage)
|
||||
tracing.SetStatusErrorf(forwardReq.Context(), logMessage)
|
||||
observability.SetStatusErrorf(forwardReq.Context(), logMessage)
|
||||
|
||||
rw.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -160,7 +162,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||
if readError != nil {
|
||||
logMessage := fmt.Sprintf("Error reading body %s. Cause: %s", fa.address, readError)
|
||||
logger.Debug().Msg(logMessage)
|
||||
tracing.SetStatusErrorf(forwardReq.Context(), logMessage)
|
||||
observability.SetStatusErrorf(forwardReq.Context(), logMessage)
|
||||
|
||||
rw.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -187,7 +189,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||
if !errors.Is(err, http.ErrNoLocation) {
|
||||
logMessage := fmt.Sprintf("Error reading response location header %s. Cause: %s", fa.address, err)
|
||||
logger.Debug().Msg(logMessage)
|
||||
tracing.SetStatusErrorf(forwardReq.Context(), logMessage)
|
||||
observability.SetStatusErrorf(forwardReq.Context(), logMessage)
|
||||
|
||||
rw.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -197,7 +199,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||
rw.Header().Set("Location", redirectURL.String())
|
||||
}
|
||||
|
||||
tracing.LogResponseCode(forwardSpan, forwardResponse.StatusCode, trace.SpanKindClient)
|
||||
tracer.CaptureResponse(forwardSpan, forwardResponse.Header, forwardResponse.StatusCode, trace.SpanKindClient)
|
||||
rw.WriteHeader(forwardResponse.StatusCode)
|
||||
|
||||
if _, err = rw.Write(body); err != nil {
|
||||
|
@ -228,7 +230,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
tracing.LogResponseCode(forwardSpan, forwardResponse.StatusCode, trace.SpanKindClient)
|
||||
tracer.CaptureResponse(forwardSpan, forwardResponse.Header, forwardResponse.StatusCode, trace.SpanKindClient)
|
||||
|
||||
req.RequestURI = req.URL.RequestURI()
|
||||
|
||||
|
|
|
@ -4,27 +4,25 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/containous/alice"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v3/pkg/config/static"
|
||||
tracingMiddleware "github.com/traefik/traefik/v3/pkg/middlewares/tracing"
|
||||
"github.com/traefik/traefik/v3/pkg/testhelpers"
|
||||
"github.com/traefik/traefik/v3/pkg/tracing"
|
||||
"github.com/traefik/traefik/v3/pkg/tracing/opentelemetry"
|
||||
"github.com/traefik/traefik/v3/pkg/types"
|
||||
"github.com/traefik/traefik/v3/pkg/version"
|
||||
"github.com/vulcand/oxy/v2/forward"
|
||||
"go.opentelemetry.io/contrib/propagators/autoprop"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||
"go.opentelemetry.io/otel/sdk/trace/tracetest"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/embedded"
|
||||
)
|
||||
|
||||
func TestForwardAuthFail(t *testing.T) {
|
||||
|
@ -466,64 +464,146 @@ func Test_writeHeader(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestForwardAuthUsesTracing(t *testing.T) {
|
||||
func TestForwardAuthTracing(t *testing.T) {
|
||||
type expected struct {
|
||||
name string
|
||||
attributes []attribute.KeyValue
|
||||
}
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("Traceparent") == "" {
|
||||
t.Errorf("expected Traceparent header to be present in request")
|
||||
}
|
||||
|
||||
w.Header().Set("X-Bar", "foo")
|
||||
w.Header().Add("X-Bar", "bar")
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
next := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
||||
|
||||
auth := dynamic.ForwardAuth{
|
||||
Address: server.URL,
|
||||
}
|
||||
|
||||
exporter := tracetest.NewInMemoryExporter()
|
||||
|
||||
tres, err := resource.New(context.Background(),
|
||||
resource.WithAttributes(semconv.ServiceNameKey.String("traefik")),
|
||||
resource.WithAttributes(semconv.ServiceVersionKey.String(version.Version)),
|
||||
resource.WithFromEnv(),
|
||||
resource.WithTelemetrySDK(),
|
||||
)
|
||||
parse, err := url.Parse(server.URL)
|
||||
require.NoError(t, err)
|
||||
|
||||
tracerProvider := sdktrace.NewTracerProvider(
|
||||
sdktrace.WithSampler(sdktrace.AlwaysSample()),
|
||||
sdktrace.WithResource(tres),
|
||||
sdktrace.WithBatcher(exporter),
|
||||
)
|
||||
otel.SetTracerProvider(tracerProvider)
|
||||
_, serverPort, err := net.SplitHostPort(parse.Host)
|
||||
require.NoError(t, err)
|
||||
|
||||
config := &static.Tracing{
|
||||
ServiceName: "testApp",
|
||||
SampleRate: 1,
|
||||
OTLP: &opentelemetry.Config{
|
||||
HTTP: &types.OtelHTTP{
|
||||
Endpoint: "http://127.0.0.1:8080",
|
||||
serverPortInt, err := strconv.Atoi(serverPort)
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
expected []expected
|
||||
}{
|
||||
{
|
||||
desc: "basic test",
|
||||
expected: []expected{
|
||||
{
|
||||
name: "initial",
|
||||
attributes: []attribute.KeyValue{
|
||||
attribute.String("span.kind", "unspecified"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "AuthRequest",
|
||||
attributes: []attribute.KeyValue{
|
||||
attribute.String("span.kind", "client"),
|
||||
attribute.String("http.request.method", "GET"),
|
||||
attribute.String("network.protocol.version", "1.1"),
|
||||
attribute.String("url.full", server.URL),
|
||||
attribute.String("url.scheme", "http"),
|
||||
attribute.String("user_agent.original", ""),
|
||||
attribute.String("network.peer.address", "127.0.0.1"),
|
||||
attribute.String("network.peer.port", serverPort),
|
||||
attribute.String("server.address", "127.0.0.1"),
|
||||
attribute.Int64("server.port", int64(serverPortInt)),
|
||||
attribute.StringSlice("http.request.header.x-foo", []string{"foo", "bar"}),
|
||||
attribute.Int64("http.response.status_code", int64(404)),
|
||||
attribute.StringSlice("http.response.header.x-bar", []string{"foo", "bar"}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
tr, closer, err := tracing.NewTracing(config)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
_ = closer.Close()
|
||||
})
|
||||
|
||||
next, err = NewForward(context.Background(), next, auth, "authTest")
|
||||
require.NoError(t, err)
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
next := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
||||
|
||||
chain := alice.New(tracingMiddleware.WrapEntryPointHandler(context.Background(), tr, "tracingTest"))
|
||||
next, err = chain.Then(next)
|
||||
require.NoError(t, err)
|
||||
auth := dynamic.ForwardAuth{
|
||||
Address: server.URL,
|
||||
AuthRequestHeaders: []string{"X-Foo"},
|
||||
}
|
||||
next, err := NewForward(context.Background(), next, auth, "authTest")
|
||||
require.NoError(t, err)
|
||||
|
||||
ts := httptest.NewServer(next)
|
||||
t.Cleanup(ts.Close)
|
||||
req := httptest.NewRequest(http.MethodGet, "http://www.test.com/search?q=Opentelemetry", nil)
|
||||
req.RemoteAddr = "10.0.0.1:1234"
|
||||
req.Header.Set("User-Agent", "forward-test")
|
||||
req.Header.Set("X-Forwarded-Proto", "http")
|
||||
req.Header.Set("X-Foo", "foo")
|
||||
req.Header.Add("X-Foo", "bar")
|
||||
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil)
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||
otel.SetTextMapPropagator(autoprop.NewTextMapPropagator())
|
||||
|
||||
mockTracer := &mockTracer{}
|
||||
tracer := tracing.NewTracer(mockTracer, []string{"X-Foo"}, []string{"X-Bar"})
|
||||
initialCtx, initialSpan := tracer.Start(req.Context(), "initial")
|
||||
defer initialSpan.End()
|
||||
req = req.WithContext(initialCtx)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
next.ServeHTTP(recorder, req)
|
||||
|
||||
for i, span := range mockTracer.spans {
|
||||
assert.Equal(t, test.expected[i].name, span.name)
|
||||
assert.Equal(t, test.expected[i].attributes, span.attributes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type mockTracer struct {
|
||||
embedded.Tracer
|
||||
|
||||
spans []*mockSpan
|
||||
}
|
||||
|
||||
var _ trace.Tracer = &mockTracer{}
|
||||
|
||||
func (t *mockTracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
|
||||
config := trace.NewSpanStartConfig(opts...)
|
||||
span := &mockSpan{}
|
||||
span.SetName(name)
|
||||
span.SetAttributes(attribute.String("span.kind", config.SpanKind().String()))
|
||||
span.SetAttributes(config.Attributes()...)
|
||||
t.spans = append(t.spans, span)
|
||||
return trace.ContextWithSpan(ctx, span), span
|
||||
}
|
||||
|
||||
// mockSpan is an implementation of Span that preforms no operations.
|
||||
type mockSpan struct {
|
||||
embedded.Span
|
||||
|
||||
name string
|
||||
attributes []attribute.KeyValue
|
||||
}
|
||||
|
||||
var _ trace.Span = &mockSpan{}
|
||||
|
||||
func (*mockSpan) SpanContext() trace.SpanContext {
|
||||
return trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID{1}, SpanID: trace.SpanID{1}})
|
||||
}
|
||||
func (*mockSpan) IsRecording() bool { return false }
|
||||
func (s *mockSpan) SetStatus(_ codes.Code, _ string) {}
|
||||
func (s *mockSpan) SetAttributes(kv ...attribute.KeyValue) {
|
||||
s.attributes = append(s.attributes, kv...)
|
||||
}
|
||||
func (s *mockSpan) End(...trace.SpanEndOption) {}
|
||||
func (s *mockSpan) RecordError(_ error, _ ...trace.EventOption) {}
|
||||
func (s *mockSpan) AddEvent(_ string, _ ...trace.EventOption) {}
|
||||
|
||||
func (s *mockSpan) SetName(name string) { s.name = name }
|
||||
|
||||
func (s *mockSpan) TracerProvider() trace.TracerProvider {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -48,7 +48,6 @@ func TestBuffering(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -149,7 +149,7 @@ func BenchmarkCapture(b *testing.B) {
|
|||
b.ReportAllocs()
|
||||
b.SetBytes(int64(test.size))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for range b.N {
|
||||
runBenchmark(b, test.size, req, handlers)
|
||||
}
|
||||
})
|
||||
|
@ -170,7 +170,7 @@ func runBenchmark(b *testing.B, size int, req *http.Request, handler http.Handle
|
|||
|
||||
func generateBytes(length int) []byte {
|
||||
var value []byte
|
||||
for i := 0; i < length; i++ {
|
||||
for i := range length {
|
||||
value = append(value, 0x61+byte(i%26))
|
||||
}
|
||||
return value
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v3/pkg/logs"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares"
|
||||
"github.com/traefik/traefik/v3/pkg/tracing"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/observability"
|
||||
"github.com/vulcand/oxy/v2/cbreaker"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
@ -34,7 +34,7 @@ func New(ctx context.Context, next http.Handler, confCircuitBreaker dynamic.Circ
|
|||
|
||||
cbOpts := []cbreaker.Option{
|
||||
cbreaker.Fallback(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
tracing.SetStatusErrorf(req.Context(), "blocked by circuit-breaker (%q)", expression)
|
||||
observability.SetStatusErrorf(req.Context(), "blocked by circuit-breaker (%q)", expression)
|
||||
rw.WriteHeader(responseCode)
|
||||
|
||||
if _, err := rw.Write([]byte(http.StatusText(responseCode))); err != nil {
|
||||
|
|
|
@ -88,7 +88,6 @@ func Test_NoBody(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -347,7 +346,6 @@ func Test_ExcludedContentTypes(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -452,7 +450,6 @@ func Test_IncludedContentTypes(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -557,7 +554,6 @@ func Test_FlushExcludedContentTypes(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -676,7 +672,6 @@ func Test_FlushIncludedContentTypes(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -831,8 +826,6 @@ func TestParseContentType_equals(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -74,8 +74,6 @@ func TestNegotiation(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -299,7 +297,6 @@ func TestShouldNotCompressWhenSpecificContentType(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -351,7 +348,6 @@ func TestShouldCompressWhenSpecificContentType(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -547,8 +543,6 @@ func TestMinResponseBodyBytes(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -720,7 +714,7 @@ func BenchmarkCompress(b *testing.B) {
|
|||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for range b.N {
|
||||
runBenchmark(b, req, handler)
|
||||
}
|
||||
})
|
||||
|
@ -741,7 +735,7 @@ func runBenchmark(b *testing.B, req *http.Request, handler http.Handler) {
|
|||
|
||||
func generateBytes(length int) []byte {
|
||||
var value []byte
|
||||
for i := 0; i < length; i++ {
|
||||
for i := range length {
|
||||
value = append(value, 0x61+byte(i))
|
||||
}
|
||||
return value
|
||||
|
|
|
@ -47,7 +47,6 @@ func TestRemover(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -46,7 +46,6 @@ func TestAutoDetection(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
|
||||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares"
|
||||
"github.com/traefik/traefik/v3/pkg/tracing"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/observability"
|
||||
"github.com/traefik/traefik/v3/pkg/types"
|
||||
"github.com/vulcand/oxy/v2/utils"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
@ -71,7 +71,7 @@ func (c *customErrors) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||
|
||||
if c.backendHandler == nil {
|
||||
logger.Error().Msg("Error pages: no backend handler.")
|
||||
tracing.SetStatusErrorf(req.Context(), "Error pages: no backend handler.")
|
||||
observability.SetStatusErrorf(req.Context(), "Error pages: no backend handler.")
|
||||
c.next.ServeHTTP(rw, req)
|
||||
return
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ func (c *customErrors) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||
pageReq, err := newRequest("http://" + req.Host + query)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Send()
|
||||
tracing.SetStatusErrorf(req.Context(), err.Error())
|
||||
observability.SetStatusErrorf(req.Context(), err.Error())
|
||||
http.Error(rw, http.StatusText(code), code)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -157,7 +157,6 @@ func TestHandler(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -272,7 +272,6 @@ func TestServeHTTP(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -369,7 +368,6 @@ func Test_isWebsocketRequest(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -52,7 +52,6 @@ func TestNewHeader_customRequestHeader(t *testing.T) {
|
|||
emptyHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -59,7 +59,6 @@ func TestNew_allowedHosts(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -67,7 +67,6 @@ func Test_newSecure_modifyResponse(t *testing.T) {
|
|||
emptyHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v3/pkg/ip"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares"
|
||||
"github.com/traefik/traefik/v3/pkg/tracing"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/observability"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
|
@ -78,7 +78,7 @@ func (al *ipAllowLister) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||
if err != nil {
|
||||
msg := fmt.Sprintf("Rejecting IP %s: %v", clientIP, err)
|
||||
logger.Debug().Msg(msg)
|
||||
tracing.SetStatusErrorf(req.Context(), msg)
|
||||
observability.SetStatusErrorf(req.Context(), msg)
|
||||
reject(ctx, al.rejectStatusCode, rw)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -41,7 +41,6 @@ func TestNewIPAllowLister(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -102,7 +101,6 @@ func TestIPAllowLister_ServeHTTP(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v3/pkg/ip"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares"
|
||||
"github.com/traefik/traefik/v3/pkg/tracing"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/observability"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
|
@ -68,7 +68,7 @@ func (wl *ipWhiteLister) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||
if err != nil {
|
||||
msg := fmt.Sprintf("Rejecting IP %s: %v", clientIP, err)
|
||||
logger.Debug().Msg(msg)
|
||||
tracing.SetStatusErrorf(req.Context(), msg)
|
||||
observability.SetStatusErrorf(req.Context(), msg)
|
||||
reject(ctx, rw)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -33,7 +33,6 @@ func TestNewIPWhiteLister(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -76,7 +75,6 @@ func TestIPWhiteLister_ServeHTTP(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package metrics
|
|||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -14,9 +15,9 @@ import (
|
|||
"github.com/traefik/traefik/v3/pkg/metrics"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/capture"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/observability"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/retry"
|
||||
traefiktls "github.com/traefik/traefik/v3/pkg/tls"
|
||||
"github.com/traefik/traefik/v3/pkg/tracing"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
@ -144,7 +145,7 @@ func (m *metricsMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request)
|
|||
}
|
||||
logger := with.Logger()
|
||||
logger.Error().Err(err).Msg("Could not get Capture")
|
||||
tracing.SetStatusErrorf(req.Context(), "Could not get Capture")
|
||||
observability.SetStatusErrorf(req.Context(), "Could not get Capture")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -216,12 +217,10 @@ func grpcStatusCode(rw http.ResponseWriter) int {
|
|||
|
||||
func containsHeader(req *http.Request, name, value string) bool {
|
||||
items := strings.Split(req.Header.Get(name), ",")
|
||||
for _, item := range items {
|
||||
if value == strings.ToLower(strings.TrimSpace(item)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
return slices.ContainsFunc(items, func(item string) bool {
|
||||
return value == strings.ToLower(strings.TrimSpace(item))
|
||||
})
|
||||
}
|
||||
|
||||
// getMethod returns the request's method.
|
||||
|
|
|
@ -80,7 +80,6 @@ func Test_getMethod(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.method, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -132,7 +131,6 @@ func Test_getRequestProtocol(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -175,7 +173,6 @@ func Test_grpcStatusCode(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
98
pkg/middlewares/observability/entrypoint.go
Normal file
98
pkg/middlewares/observability/entrypoint.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
package observability
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containous/alice"
|
||||
"github.com/traefik/traefik/v3/pkg/metrics"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares"
|
||||
"github.com/traefik/traefik/v3/pkg/tracing"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
)
|
||||
|
||||
const (
|
||||
entryPointTypeName = "TracingEntryPoint"
|
||||
)
|
||||
|
||||
type entryPointTracing struct {
|
||||
tracer *tracing.Tracer
|
||||
|
||||
entryPoint string
|
||||
next http.Handler
|
||||
semConvMetricRegistry *metrics.SemConvMetricsRegistry
|
||||
}
|
||||
|
||||
// WrapEntryPointHandler Wraps tracing to alice.Constructor.
|
||||
func WrapEntryPointHandler(ctx context.Context, tracer *tracing.Tracer, semConvMetricRegistry *metrics.SemConvMetricsRegistry, entryPointName string) alice.Constructor {
|
||||
return func(next http.Handler) (http.Handler, error) {
|
||||
if tracer == nil {
|
||||
tracer = tracing.NewTracer(noop.Tracer{}, nil, nil)
|
||||
}
|
||||
|
||||
return newEntryPoint(ctx, tracer, semConvMetricRegistry, entryPointName, next), nil
|
||||
}
|
||||
}
|
||||
|
||||
// newEntryPoint creates a new tracing middleware for incoming requests.
|
||||
func newEntryPoint(ctx context.Context, tracer *tracing.Tracer, semConvMetricRegistry *metrics.SemConvMetricsRegistry, entryPointName string, next http.Handler) http.Handler {
|
||||
middlewares.GetLogger(ctx, "tracing", entryPointTypeName).Debug().Msg("Creating middleware")
|
||||
|
||||
if tracer == nil {
|
||||
tracer = tracing.NewTracer(noop.Tracer{}, nil, nil)
|
||||
}
|
||||
|
||||
return &entryPointTracing{
|
||||
entryPoint: entryPointName,
|
||||
tracer: tracer,
|
||||
semConvMetricRegistry: semConvMetricRegistry,
|
||||
next: next,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *entryPointTracing) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
tracingCtx := tracing.ExtractCarrierIntoContext(req.Context(), req.Header)
|
||||
start := time.Now()
|
||||
tracingCtx, span := e.tracer.Start(tracingCtx, "EntryPoint", trace.WithSpanKind(trace.SpanKindServer), trace.WithTimestamp(start))
|
||||
|
||||
req = req.WithContext(tracingCtx)
|
||||
|
||||
span.SetAttributes(attribute.String("entry_point", e.entryPoint))
|
||||
|
||||
e.tracer.CaptureServerRequest(span, req)
|
||||
|
||||
recorder := newStatusCodeRecorder(rw, http.StatusOK)
|
||||
e.next.ServeHTTP(recorder, req)
|
||||
|
||||
e.tracer.CaptureResponse(span, recorder.Header(), recorder.Status(), trace.SpanKindServer)
|
||||
|
||||
end := time.Now()
|
||||
span.End(trace.WithTimestamp(end))
|
||||
|
||||
if e.semConvMetricRegistry != nil && e.semConvMetricRegistry.HTTPServerRequestDuration() != nil {
|
||||
var attrs []attribute.KeyValue
|
||||
|
||||
if recorder.Status() < 100 || recorder.Status() >= 600 {
|
||||
attrs = append(attrs, attribute.Key("error.type").String(fmt.Sprintf("Invalid HTTP status code ; %d", recorder.Status())))
|
||||
} else if recorder.Status() >= 400 {
|
||||
attrs = append(attrs, attribute.Key("error.type").String(strconv.Itoa(recorder.Status())))
|
||||
}
|
||||
|
||||
attrs = append(attrs, semconv.HTTPRequestMethodKey.String(req.Method))
|
||||
attrs = append(attrs, semconv.HTTPResponseStatusCode(recorder.Status()))
|
||||
attrs = append(attrs, semconv.NetworkProtocolName(strings.ToLower(req.Proto)))
|
||||
attrs = append(attrs, semconv.NetworkProtocolVersion(Proto(req.Proto)))
|
||||
attrs = append(attrs, semconv.ServerAddress(req.Host))
|
||||
attrs = append(attrs, semconv.URLScheme(req.Header.Get("X-Forwarded-Proto")))
|
||||
|
||||
e.semConvMetricRegistry.HTTPServerRequestDuration().Record(req.Context(), end.Sub(start).Seconds(), metric.WithAttributes(attrs...))
|
||||
}
|
||||
}
|
184
pkg/middlewares/observability/entrypoint_test.go
Normal file
184
pkg/middlewares/observability/entrypoint_test.go
Normal file
|
@ -0,0 +1,184 @@
|
|||
package observability
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
ptypes "github.com/traefik/paerser/types"
|
||||
"github.com/traefik/traefik/v3/pkg/metrics"
|
||||
"github.com/traefik/traefik/v3/pkg/tracing"
|
||||
"github.com/traefik/traefik/v3/pkg/types"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest"
|
||||
)
|
||||
|
||||
func TestEntryPointMiddleware_tracing(t *testing.T) {
|
||||
type expected struct {
|
||||
name string
|
||||
attributes []attribute.KeyValue
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
entryPoint string
|
||||
expected expected
|
||||
}{
|
||||
{
|
||||
desc: "basic test",
|
||||
entryPoint: "test",
|
||||
expected: expected{
|
||||
name: "EntryPoint",
|
||||
attributes: []attribute.KeyValue{
|
||||
attribute.String("span.kind", "server"),
|
||||
attribute.String("entry_point", "test"),
|
||||
attribute.String("http.request.method", "GET"),
|
||||
attribute.String("network.protocol.version", "1.1"),
|
||||
attribute.Int64("http.request.body.size", int64(0)),
|
||||
attribute.String("url.path", "/search"),
|
||||
attribute.String("url.query", "q=Opentelemetry"),
|
||||
attribute.String("url.scheme", "http"),
|
||||
attribute.String("user_agent.original", "entrypoint-test"),
|
||||
attribute.String("server.address", "www.test.com"),
|
||||
attribute.String("network.peer.address", "10.0.0.1"),
|
||||
attribute.String("network.peer.port", "1234"),
|
||||
attribute.String("client.address", "10.0.0.1"),
|
||||
attribute.Int64("client.port", int64(1234)),
|
||||
attribute.String("client.socket.address", ""),
|
||||
attribute.StringSlice("http.request.header.x-foo", []string{"foo", "bar"}),
|
||||
attribute.Int64("http.response.status_code", int64(404)),
|
||||
attribute.StringSlice("http.response.header.x-bar", []string{"foo", "bar"}),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "http://www.test.com/search?q=Opentelemetry", nil)
|
||||
rw := httptest.NewRecorder()
|
||||
req.RemoteAddr = "10.0.0.1:1234"
|
||||
req.Header.Set("User-Agent", "entrypoint-test")
|
||||
req.Header.Set("X-Forwarded-Proto", "http")
|
||||
req.Header.Set("X-Foo", "foo")
|
||||
req.Header.Add("X-Foo", "bar")
|
||||
|
||||
next := http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
|
||||
rw.Header().Set("X-Bar", "foo")
|
||||
rw.Header().Add("X-Bar", "bar")
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
})
|
||||
|
||||
tracer := &mockTracer{}
|
||||
|
||||
handler := newEntryPoint(context.Background(), tracing.NewTracer(tracer, []string{"X-Foo"}, []string{"X-Bar"}), nil, test.entryPoint, next)
|
||||
handler.ServeHTTP(rw, req)
|
||||
|
||||
for _, span := range tracer.spans {
|
||||
assert.Equal(t, test.expected.name, span.name)
|
||||
assert.Equal(t, test.expected.attributes, span.attributes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEntryPointMiddleware_metrics(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
statusCode int
|
||||
wantAttributes attribute.Set
|
||||
}{
|
||||
{
|
||||
desc: "not found status",
|
||||
statusCode: http.StatusNotFound,
|
||||
wantAttributes: attribute.NewSet(
|
||||
attribute.Key("error.type").String("404"),
|
||||
attribute.Key("http.request.method").String("GET"),
|
||||
attribute.Key("http.response.status_code").Int(404),
|
||||
attribute.Key("network.protocol.name").String("http/1.1"),
|
||||
attribute.Key("network.protocol.version").String("1.1"),
|
||||
attribute.Key("server.address").String("www.test.com"),
|
||||
attribute.Key("url.scheme").String("http"),
|
||||
),
|
||||
},
|
||||
{
|
||||
desc: "created status",
|
||||
statusCode: http.StatusCreated,
|
||||
wantAttributes: attribute.NewSet(
|
||||
attribute.Key("http.request.method").String("GET"),
|
||||
attribute.Key("http.response.status_code").Int(201),
|
||||
attribute.Key("network.protocol.name").String("http/1.1"),
|
||||
attribute.Key("network.protocol.version").String("1.1"),
|
||||
attribute.Key("server.address").String("www.test.com"),
|
||||
attribute.Key("url.scheme").String("http"),
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var cfg types.OTLP
|
||||
(&cfg).SetDefaults()
|
||||
cfg.AddRoutersLabels = true
|
||||
cfg.PushInterval = ptypes.Duration(10 * time.Millisecond)
|
||||
rdr := sdkmetric.NewManualReader()
|
||||
|
||||
meterProvider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(rdr))
|
||||
// force the meter provider with manual reader to collect metrics for the test.
|
||||
metrics.SetMeterProvider(meterProvider)
|
||||
|
||||
semConvMetricRegistry, err := metrics.NewSemConvMetricRegistry(context.Background(), &cfg)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, semConvMetricRegistry)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "http://www.test.com/search?q=Opentelemetry", nil)
|
||||
rw := httptest.NewRecorder()
|
||||
req.RemoteAddr = "10.0.0.1:1234"
|
||||
req.Header.Set("User-Agent", "entrypoint-test")
|
||||
req.Header.Set("X-Forwarded-Proto", "http")
|
||||
|
||||
next := http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
|
||||
rw.WriteHeader(test.statusCode)
|
||||
})
|
||||
|
||||
handler := newEntryPoint(context.Background(), nil, semConvMetricRegistry, "test", next)
|
||||
handler.ServeHTTP(rw, req)
|
||||
|
||||
got := metricdata.ResourceMetrics{}
|
||||
err = rdr.Collect(context.Background(), &got)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, got.ScopeMetrics, 1)
|
||||
|
||||
expected := metricdata.Metrics{
|
||||
Name: "http.server.request.duration",
|
||||
Description: "Duration of HTTP server requests.",
|
||||
Unit: "s",
|
||||
Data: metricdata.Histogram[float64]{
|
||||
DataPoints: []metricdata.HistogramDataPoint[float64]{
|
||||
{
|
||||
Attributes: test.wantAttributes,
|
||||
Count: 1,
|
||||
Bounds: []float64{0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10},
|
||||
BucketCounts: []uint64{0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
|
||||
Min: metricdata.NewExtrema[float64](1),
|
||||
Max: metricdata.NewExtrema[float64](1),
|
||||
Sum: 1,
|
||||
},
|
||||
},
|
||||
Temporality: metricdata.CumulativeTemporality,
|
||||
},
|
||||
}
|
||||
|
||||
metricdatatest.AssertEqual[metricdata.Metrics](t, expected, got.ScopeMetrics[0].Metrics[0], metricdatatest.IgnoreTimestamp(), metricdatatest.IgnoreValue())
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package tracing
|
||||
package observability
|
||||
|
||||
import (
|
||||
"context"
|
|
@ -1,4 +1,4 @@
|
|||
package tracing
|
||||
package observability
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -47,7 +47,9 @@ type mockSpan struct {
|
|||
|
||||
var _ trace.Span = &mockSpan{}
|
||||
|
||||
func (*mockSpan) SpanContext() trace.SpanContext { return trace.SpanContext{} }
|
||||
func (*mockSpan) SpanContext() trace.SpanContext {
|
||||
return trace.NewSpanContext(trace.SpanContextConfig{TraceID: trace.TraceID{1}, SpanID: trace.SpanID{1}})
|
||||
}
|
||||
func (*mockSpan) IsRecording() bool { return false }
|
||||
func (s *mockSpan) SetStatus(_ codes.Code, _ string) {}
|
||||
func (s *mockSpan) SetAttributes(kv ...attribute.KeyValue) {
|
||||
|
@ -59,4 +61,6 @@ func (s *mockSpan) AddEvent(_ string, _ ...trace.EventOption) {}
|
|||
|
||||
func (s *mockSpan) SetName(name string) { s.name = name }
|
||||
|
||||
func (*mockSpan) TracerProvider() trace.TracerProvider { return mockTracerProvider{} }
|
||||
func (s *mockSpan) TracerProvider() trace.TracerProvider {
|
||||
return nil
|
||||
}
|
31
pkg/middlewares/observability/observability.go
Normal file
31
pkg/middlewares/observability/observability.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package observability
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
// SetStatusErrorf flags the span as in error and log an event.
|
||||
func SetStatusErrorf(ctx context.Context, format string, args ...interface{}) {
|
||||
if span := trace.SpanFromContext(ctx); span != nil {
|
||||
span.SetStatus(codes.Error, fmt.Sprintf(format, args...))
|
||||
}
|
||||
}
|
||||
|
||||
func Proto(proto string) string {
|
||||
switch proto {
|
||||
case "HTTP/1.0":
|
||||
return "1.0"
|
||||
case "HTTP/1.1":
|
||||
return "1.1"
|
||||
case "HTTP/2":
|
||||
return "2"
|
||||
case "HTTP/3":
|
||||
return "3"
|
||||
default:
|
||||
return proto
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package tracing
|
||||
package observability
|
||||
|
||||
import (
|
||||
"context"
|
|
@ -1,4 +1,4 @@
|
|||
package tracing
|
||||
package observability
|
||||
|
||||
import (
|
||||
"context"
|
|
@ -1,4 +1,4 @@
|
|||
package tracing
|
||||
package observability
|
||||
|
||||
import (
|
||||
"context"
|
|
@ -1,4 +1,4 @@
|
|||
package tracing
|
||||
package observability
|
||||
|
||||
import (
|
||||
"context"
|
|
@ -1,4 +1,4 @@
|
|||
package tracing
|
||||
package observability
|
||||
|
||||
import (
|
||||
"bufio"
|
|
@ -310,7 +310,6 @@ func TestPassTLSClientCert_PEM(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -533,7 +532,6 @@ func TestPassTLSClientCert_certInfo(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -604,7 +602,6 @@ WqeUSNGYV//RunTeuRDAf5OxehERb1srzBXhRZ3cZdzXbgR/`,
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -645,7 +642,6 @@ func Test_getSANs(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares"
|
||||
"github.com/traefik/traefik/v3/pkg/tracing"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/observability"
|
||||
"github.com/vulcand/oxy/v2/utils"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"golang.org/x/time/rate"
|
||||
|
@ -153,14 +153,14 @@ func (rl *rateLimiter) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||
// as the expiryTime is supposed to reflect the activity (or lack thereof) on that source.
|
||||
if err := rl.buckets.Set(source, bucket, rl.ttl); err != nil {
|
||||
logger.Error().Err(err).Msg("Could not insert/update bucket")
|
||||
tracing.SetStatusErrorf(req.Context(), "Could not insert/update bucket")
|
||||
observability.SetStatusErrorf(req.Context(), "Could not insert/update bucket")
|
||||
http.Error(rw, "could not insert/update bucket", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
res := bucket.Reserve()
|
||||
if !res.OK() {
|
||||
tracing.SetStatusErrorf(req.Context(), "No bursty traffic allowed")
|
||||
observability.SetStatusErrorf(req.Context(), "No bursty traffic allowed")
|
||||
http.Error(rw, "No bursty traffic allowed", http.StatusTooManyRequests)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -89,7 +89,6 @@ func TestNewRateLimiter(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -248,7 +247,6 @@ func TestRateLimit(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
if test.loadDuration >= time.Minute && testing.Short() {
|
||||
t.Skip("skipping test in short mode.")
|
||||
|
|
|
@ -154,7 +154,6 @@ func TestRedirectRegexHandler(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -283,8 +283,6 @@ func TestRedirectSchemeHandler(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
|
||||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares"
|
||||
"github.com/traefik/traefik/v3/pkg/tracing"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/observability"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
|
@ -52,7 +52,7 @@ func (r *replacePath) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||
req.URL.Path, err = url.PathUnescape(req.URL.RawPath)
|
||||
if err != nil {
|
||||
middlewares.GetLogger(context.Background(), r.name, typeName).Error().Err(err).Send()
|
||||
tracing.SetStatusErrorf(req.Context(), err.Error())
|
||||
observability.SetStatusErrorf(req.Context(), err.Error())
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ import (
|
|||
|
||||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/observability"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/replacepath"
|
||||
"github.com/traefik/traefik/v3/pkg/tracing"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
|
@ -52,7 +52,7 @@ func (rp *replacePathRegex) ServeHTTP(rw http.ResponseWriter, req *http.Request)
|
|||
currentPath = req.URL.EscapedPath()
|
||||
}
|
||||
|
||||
if rp.regexp != nil && len(rp.replacement) > 0 && rp.regexp.MatchString(currentPath) {
|
||||
if rp.regexp != nil && rp.regexp.MatchString(currentPath) {
|
||||
req.Header.Add(replacepath.ReplacedPathHeader, currentPath)
|
||||
req.URL.RawPath = rp.regexp.ReplaceAllString(currentPath, rp.replacement)
|
||||
|
||||
|
@ -63,7 +63,7 @@ func (rp *replacePathRegex) ServeHTTP(rw http.ResponseWriter, req *http.Request)
|
|||
req.URL.Path, err = url.PathUnescape(req.URL.RawPath)
|
||||
if err != nil {
|
||||
middlewares.GetLogger(context.Background(), rp.name, typeName).Error().Err(err).Send()
|
||||
tracing.SetStatusErrorf(req.Context(), err.Error())
|
||||
observability.SetStatusErrorf(req.Context(), err.Error())
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -44,6 +44,28 @@ func TestReplacePathRegex(t *testing.T) {
|
|||
expectedRawPath: "/who-am-i/and/who-am-i",
|
||||
expectedHeader: "/whoami/and/whoami",
|
||||
},
|
||||
{
|
||||
desc: "empty replacement",
|
||||
path: "/whoami/and/whoami",
|
||||
config: dynamic.ReplacePathRegex{
|
||||
Replacement: "",
|
||||
Regex: `/whoami`,
|
||||
},
|
||||
expectedPath: "/and",
|
||||
expectedRawPath: "/and",
|
||||
expectedHeader: "/whoami/and/whoami",
|
||||
},
|
||||
{
|
||||
desc: "empty trimmed replacement",
|
||||
path: "/whoami/and/whoami",
|
||||
config: dynamic.ReplacePathRegex{
|
||||
Replacement: " ",
|
||||
Regex: `/whoami`,
|
||||
},
|
||||
expectedPath: "/and",
|
||||
expectedRawPath: "/and",
|
||||
expectedHeader: "/whoami/and/whoami",
|
||||
},
|
||||
{
|
||||
desc: "no match",
|
||||
path: "/whoami/and/whoami",
|
||||
|
|
|
@ -49,7 +49,7 @@ func (hr *Resolver) CNAMEFlatten(ctx context.Context, host string) string {
|
|||
|
||||
logger := log.Ctx(ctx)
|
||||
cacheDuration := 0 * time.Second
|
||||
for depth := 0; depth < hr.ResolvDepth; depth++ {
|
||||
for depth := range hr.ResolvDepth {
|
||||
resolv, err := cnameResolve(ctx, request, hr.ResolvConfig)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Send()
|
||||
|
|
|
@ -35,7 +35,6 @@ func TestCNAMEFlatten(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -38,7 +38,6 @@ func TestRequestHost(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -80,7 +79,6 @@ func TestRequestFlattening(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -173,7 +171,6 @@ func Test_parseHost(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -101,7 +101,6 @@ func TestRetry(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -203,7 +202,7 @@ func TestMultipleRetriesShouldNotLooseHeaders(t *testing.T) {
|
|||
}
|
||||
|
||||
// Validate that we don't have headers from previous attempts
|
||||
for i := 0; i < attempt; i++ {
|
||||
for i := range attempt {
|
||||
headerName := fmt.Sprintf("X-Foo-Test-%d", i)
|
||||
headerValue = responseRecorder.Header().Get("headerName")
|
||||
if headerValue != "" {
|
||||
|
@ -272,7 +271,6 @@ func TestRetryWebsocket(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -37,7 +37,6 @@ func TestSNICheck_ServeHTTP(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -134,7 +134,6 @@ func TestStripPrefix(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -108,7 +108,6 @@ func TestStripPrefixRegex(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.path, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -39,7 +39,6 @@ func TestNewIPAllowLister(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -81,7 +80,6 @@ func TestIPAllowLister_ServeHTTP(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -39,7 +39,6 @@ func TestNewIPWhiteLister(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -81,7 +80,6 @@ func TestIPWhiteLister_ServeHTTP(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
package tracing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/containous/alice"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares"
|
||||
"github.com/traefik/traefik/v3/pkg/tracing"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
const (
|
||||
entryPointTypeName = "TracingEntryPoint"
|
||||
)
|
||||
|
||||
type entryPointTracing struct {
|
||||
tracer trace.Tracer
|
||||
entryPoint string
|
||||
next http.Handler
|
||||
}
|
||||
|
||||
// WrapEntryPointHandler Wraps tracing to alice.Constructor.
|
||||
func WrapEntryPointHandler(ctx context.Context, tracer trace.Tracer, entryPointName string) alice.Constructor {
|
||||
return func(next http.Handler) (http.Handler, error) {
|
||||
return newEntryPoint(ctx, tracer, entryPointName, next), nil
|
||||
}
|
||||
}
|
||||
|
||||
// newEntryPoint creates a new tracing middleware for incoming requests.
|
||||
func newEntryPoint(ctx context.Context, tracer trace.Tracer, entryPointName string, next http.Handler) http.Handler {
|
||||
middlewares.GetLogger(ctx, "tracing", entryPointTypeName).Debug().Msg("Creating middleware")
|
||||
|
||||
return &entryPointTracing{
|
||||
entryPoint: entryPointName,
|
||||
tracer: tracer,
|
||||
next: next,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *entryPointTracing) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
tracingCtx := tracing.ExtractCarrierIntoContext(req.Context(), req.Header)
|
||||
tracingCtx, span := e.tracer.Start(tracingCtx, "EntryPoint", trace.WithSpanKind(trace.SpanKindServer))
|
||||
defer span.End()
|
||||
|
||||
req = req.WithContext(tracingCtx)
|
||||
|
||||
span.SetAttributes(attribute.String("entry_point", e.entryPoint))
|
||||
|
||||
tracing.LogServerRequest(span, req)
|
||||
|
||||
recorder := newStatusCodeRecorder(rw, http.StatusOK)
|
||||
e.next.ServeHTTP(recorder, req)
|
||||
|
||||
tracing.LogResponseCode(span, recorder.Status(), trace.SpanKindServer)
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
package tracing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
)
|
||||
|
||||
func TestEntryPointMiddleware(t *testing.T) {
|
||||
type expected struct {
|
||||
name string
|
||||
attributes []attribute.KeyValue
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
entryPoint string
|
||||
expected expected
|
||||
}{
|
||||
{
|
||||
desc: "basic test",
|
||||
entryPoint: "test",
|
||||
expected: expected{
|
||||
name: "EntryPoint",
|
||||
attributes: []attribute.KeyValue{
|
||||
attribute.String("span.kind", "server"),
|
||||
attribute.String("entry_point", "test"),
|
||||
attribute.String("http.request.method", "GET"),
|
||||
attribute.String("network.protocol.version", "1.1"),
|
||||
attribute.Int64("http.request.body.size", int64(0)),
|
||||
attribute.String("url.path", "/search"),
|
||||
attribute.String("url.query", "q=Opentelemetry"),
|
||||
attribute.String("url.scheme", "http"),
|
||||
attribute.String("user_agent.original", "entrypoint-test"),
|
||||
attribute.String("server.address", "www.test.com"),
|
||||
attribute.String("network.peer.address", "10.0.0.1"),
|
||||
attribute.String("network.peer.port", "1234"),
|
||||
attribute.String("client.address", "10.0.0.1"),
|
||||
attribute.Int64("client.port", int64(1234)),
|
||||
attribute.String("client.socket.address", ""),
|
||||
attribute.Int64("http.response.status_code", int64(404)),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "http://www.test.com/search?q=Opentelemetry", nil)
|
||||
rw := httptest.NewRecorder()
|
||||
req.RemoteAddr = "10.0.0.1:1234"
|
||||
req.Header.Set("User-Agent", "entrypoint-test")
|
||||
req.Header.Set("X-Forwarded-Proto", "http")
|
||||
|
||||
next := http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
})
|
||||
|
||||
tracer := &mockTracer{}
|
||||
|
||||
handler := newEntryPoint(context.Background(), tracer, test.entryPoint, next)
|
||||
handler.ServeHTTP(rw, req)
|
||||
|
||||
for _, span := range tracer.spans {
|
||||
assert.Equal(t, test.expected.name, span.name)
|
||||
assert.Equal(t, test.expected.attributes, span.attributes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -65,7 +65,6 @@ func TestClientIPMatcher(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -139,7 +138,6 @@ func TestMethodMatcher(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -257,7 +255,6 @@ func TestHostMatcher(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -357,7 +354,6 @@ func TestHostRegexpMatcher(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -431,7 +427,6 @@ func TestPathMatcher(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -524,7 +519,6 @@ func TestPathRegexpMatcher(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -596,7 +590,6 @@ func TestPathPrefixMatcher(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -683,8 +676,6 @@ func TestHeaderMatcher(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -792,7 +783,6 @@ func TestHeaderRegexpMatcher(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -881,7 +871,6 @@ func TestQueryMatcher(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -995,7 +984,6 @@ func TestQueryRegexpMatcher(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -25,15 +25,21 @@ var httpFuncsV2 = map[string]func(*matchersTree, ...string) error{
|
|||
}
|
||||
|
||||
func pathV2(tree *matchersTree, paths ...string) error {
|
||||
var routes []*mux.Route
|
||||
|
||||
for _, path := range paths {
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
return fmt.Errorf("path %q does not start with a '/'", path)
|
||||
route := mux.NewRouter().NewRoute()
|
||||
|
||||
if err := route.Path(path).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
routes = append(routes, route)
|
||||
}
|
||||
|
||||
tree.matcher = func(req *http.Request) bool {
|
||||
for _, path := range paths {
|
||||
if req.URL.Path == path {
|
||||
for _, route := range routes {
|
||||
if route.Match(req, &mux.RouteMatch{}) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -45,15 +51,21 @@ func pathV2(tree *matchersTree, paths ...string) error {
|
|||
}
|
||||
|
||||
func pathPrefixV2(tree *matchersTree, paths ...string) error {
|
||||
var routes []*mux.Route
|
||||
|
||||
for _, path := range paths {
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
return fmt.Errorf("path %q does not start with a '/'", path)
|
||||
route := mux.NewRouter().NewRoute()
|
||||
|
||||
if err := route.PathPrefix(path).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
routes = append(routes, route)
|
||||
}
|
||||
|
||||
tree.matcher = func(req *http.Request) bool {
|
||||
for _, path := range paths {
|
||||
if strings.HasPrefix(req.URL.Path, path) {
|
||||
for _, route := range routes {
|
||||
if route.Match(req, &mux.RouteMatch{}) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,6 @@ func TestClientIPV2Matcher(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -146,7 +145,6 @@ func TestMethodV2Matcher(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -271,7 +269,6 @@ func TestHostV2Matcher(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -374,7 +371,6 @@ func TestHostRegexpV2Matcher(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -454,10 +450,21 @@ func TestPathV2Matcher(t *testing.T) {
|
|||
"https://example.com/css/main.css": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "valid Path matcher with regexp",
|
||||
rule: "Path(`/css{path:(/.*)?}`)",
|
||||
expected: map[string]int{
|
||||
"https://example.com": http.StatusNotFound,
|
||||
"https://example.com/css/main.css": http.StatusOK,
|
||||
"https://example.org/css/main.css": http.StatusOK,
|
||||
"https://example.com/css/components/component.css": http.StatusOK,
|
||||
"https://example.com/css.css": http.StatusNotFound,
|
||||
"https://example.com/js/main.js": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -535,10 +542,21 @@ func TestPathPrefixV2Matcher(t *testing.T) {
|
|||
"https://example.com/css/main.css": http.StatusOK,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "valid PathPrefix matcher with regexp",
|
||||
rule: "PathPrefix(`/css-{name:[0-9]?}`)",
|
||||
expected: map[string]int{
|
||||
"https://example.com": http.StatusNotFound,
|
||||
"https://example.com/css-1/main.css": http.StatusOK,
|
||||
"https://example.org/css-222/main.css": http.StatusOK,
|
||||
"https://example.com/css-333333/components/component.css": http.StatusOK,
|
||||
"https://example.com/css.css": http.StatusNotFound,
|
||||
"https://example.com/js/main.js": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -625,8 +643,6 @@ func TestHeadersMatcher(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -734,7 +750,6 @@ func TestHeaderRegexpV2Matcher(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -827,7 +842,6 @@ func TestHostRegexp(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -1495,8 +1509,6 @@ func Test_addRoute(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -222,8 +222,6 @@ func TestMuxer(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -378,14 +376,12 @@ func Test_addRoutePriority(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
muxer, err := NewMuxer()
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, route := range test.cases {
|
||||
route := route
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("X-From", route.xFrom)
|
||||
})
|
||||
|
@ -446,7 +442,6 @@ func TestParseDomains(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.expression, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -511,7 +506,6 @@ func TestEmptyHost(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -550,7 +544,6 @@ func TestGetRulePriority(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@ func Test_HostSNICatchAll(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -137,7 +136,6 @@ func Test_HostSNI(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -220,7 +218,6 @@ func Test_HostSNIRegexp(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -292,7 +289,6 @@ func Test_ClientIP(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -356,7 +352,6 @@ func Test_ALPN(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -461,8 +461,6 @@ func Test_addTCPRouteV2(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -561,7 +559,6 @@ func TestParseHostSNIV2(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.expression, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -606,8 +603,6 @@ func Test_HostSNICatchAllV2(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -708,8 +703,6 @@ func Test_HostSNIV2(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -823,8 +816,6 @@ func Test_HostSNIRegexpV2(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -916,8 +907,6 @@ func Test_ClientIPV2(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -985,8 +974,6 @@ func Test_ALPNV2(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue