Merge current v2.11 into v3.0

This commit is contained in:
mmatur 2024-02-08 14:15:45 +01:00
commit bc84fdd006
No known key found for this signature in database
GPG key ID: 2FFE42FC256CFF8E
143 changed files with 8736 additions and 6601 deletions

View file

@ -76,7 +76,7 @@ func New(staticConfig static.Configuration, runtimeConfig *runtime.Configuration
// createRouter creates API routes and router.
func (h Handler) createRouter() *mux.Router {
router := mux.NewRouter()
router := mux.NewRouter().UseEncodedPath()
if h.staticConfig.API.Debug {
DebugHandler{}.Append(router)

View file

@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"sort"
"strconv"
@ -49,7 +50,13 @@ func (h Handler) getEntryPoints(rw http.ResponseWriter, request *http.Request) {
}
func (h Handler) getEntryPoint(rw http.ResponseWriter, request *http.Request) {
entryPointID := mux.Vars(request)["entryPointID"]
scapedEntryPointID := mux.Vars(request)["entryPointID"]
entryPointID, err := url.PathUnescape(scapedEntryPointID)
if err != nil {
writeError(rw, fmt.Sprintf("unable to decode entryPointID %q: %s", scapedEntryPointID, err), http.StatusBadRequest)
return
}
rw.Header().Set("Content-Type", "application/json")
@ -64,7 +71,7 @@ func (h Handler) getEntryPoint(rw http.ResponseWriter, request *http.Request) {
Name: entryPointID,
}
err := json.NewEncoder(rw).Encode(result)
err = json.NewEncoder(rw).Encode(result)
if err != nil {
log.Ctx(request.Context()).Error().Err(err).Send()
writeError(rw, err.Error(), http.StatusInternalServerError)

View file

@ -6,6 +6,7 @@ import (
"io"
"net/http"
"net/http/httptest"
"net/url"
"os"
"strconv"
"testing"
@ -169,6 +170,21 @@ func TestHandler_EntryPoints(t *testing.T) {
jsonFile: "testdata/entrypoint-bar.json",
},
},
{
desc: "one entry point by id containing slash",
path: "/api/entrypoints/" + url.PathEscape("foo / bar"),
conf: static.Configuration{
Global: &static.Global{},
API: &static.API{},
EntryPoints: map[string]*static.EntryPoint{
"foo / bar": {Address: ":81"},
},
},
expected: expected{
statusCode: http.StatusOK,
jsonFile: "testdata/entrypoint-foo-slash-bar.json",
},
},
{
desc: "one entry point by id, that does not exist",
path: "/api/entrypoints/foo",

View file

@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
@ -97,7 +98,13 @@ func (h Handler) getRouters(rw http.ResponseWriter, request *http.Request) {
}
func (h Handler) getRouter(rw http.ResponseWriter, request *http.Request) {
routerID := mux.Vars(request)["routerID"]
scapedRouterID := mux.Vars(request)["routerID"]
routerID, err := url.PathUnescape(scapedRouterID)
if err != nil {
writeError(rw, fmt.Sprintf("unable to decode routerID %q: %s", scapedRouterID, err), http.StatusBadRequest)
return
}
rw.Header().Set("Content-Type", "application/json")
@ -109,7 +116,7 @@ func (h Handler) getRouter(rw http.ResponseWriter, request *http.Request) {
result := newRouterRepresentation(routerID, router)
err := json.NewEncoder(rw).Encode(result)
err = json.NewEncoder(rw).Encode(result)
if err != nil {
log.Ctx(request.Context()).Error().Err(err).Send()
writeError(rw, err.Error(), http.StatusInternalServerError)
@ -148,7 +155,13 @@ func (h Handler) getServices(rw http.ResponseWriter, request *http.Request) {
}
func (h Handler) getService(rw http.ResponseWriter, request *http.Request) {
serviceID := mux.Vars(request)["serviceID"]
scapedServiceID := mux.Vars(request)["serviceID"]
serviceID, err := url.PathUnescape(scapedServiceID)
if err != nil {
writeError(rw, fmt.Sprintf("unable to decode serviceID %q: %s", scapedServiceID, err), http.StatusBadRequest)
return
}
rw.Header().Add("Content-Type", "application/json")
@ -160,7 +173,7 @@ func (h Handler) getService(rw http.ResponseWriter, request *http.Request) {
result := newServiceRepresentation(serviceID, service)
err := json.NewEncoder(rw).Encode(result)
err = json.NewEncoder(rw).Encode(result)
if err != nil {
log.Ctx(request.Context()).Error().Err(err).Send()
writeError(rw, err.Error(), http.StatusInternalServerError)
@ -199,7 +212,13 @@ func (h Handler) getMiddlewares(rw http.ResponseWriter, request *http.Request) {
}
func (h Handler) getMiddleware(rw http.ResponseWriter, request *http.Request) {
middlewareID := mux.Vars(request)["middlewareID"]
scapedMiddlewareID := mux.Vars(request)["middlewareID"]
middlewareID, err := url.PathUnescape(scapedMiddlewareID)
if err != nil {
writeError(rw, fmt.Sprintf("unable to decode middlewareID %q: %s", scapedMiddlewareID, err), http.StatusBadRequest)
return
}
rw.Header().Set("Content-Type", "application/json")
@ -211,7 +230,7 @@ func (h Handler) getMiddleware(rw http.ResponseWriter, request *http.Request) {
result := newMiddlewareRepresentation(middlewareID, middleware)
err := json.NewEncoder(rw).Encode(result)
err = json.NewEncoder(rw).Encode(result)
if err != nil {
log.Ctx(request.Context()).Error().Err(err).Send()
writeError(rw, err.Error(), http.StatusInternalServerError)

View file

@ -7,6 +7,7 @@ import (
"io"
"net/http"
"net/http/httptest"
"net/url"
"os"
"strconv"
"testing"
@ -301,6 +302,27 @@ func TestHandler_HTTP(t *testing.T) {
jsonFile: "testdata/router-bar.json",
},
},
{
desc: "one router by id containing slash",
path: "/api/http/routers/" + url.PathEscape("foo / bar@myprovider"),
conf: runtime.Configuration{
Routers: map[string]*runtime.RouterInfo{
"foo / bar@myprovider": {
Router: &dynamic.Router{
EntryPoints: []string{"web"},
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)",
Middlewares: []string{"auth", "addPrefixTest@anotherprovider"},
},
Status: "enabled",
},
},
},
expected: expected{
statusCode: http.StatusOK,
jsonFile: "testdata/router-foo-slash-bar.json",
},
},
{
desc: "one router by id, implicitly using default TLS options",
path: "/api/http/routers/baz@myprovider",
@ -661,6 +683,35 @@ func TestHandler_HTTP(t *testing.T) {
jsonFile: "testdata/service-bar.json",
},
},
{
desc: "one service by id containing slash",
path: "/api/http/services/" + url.PathEscape("foo / bar@myprovider"),
conf: runtime.Configuration{
Services: map[string]*runtime.ServiceInfo{
"foo / bar@myprovider": func() *runtime.ServiceInfo {
si := &runtime.ServiceInfo{
Service: &dynamic.Service{
LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: Bool(true),
Servers: []dynamic.Server{
{
URL: "http://127.0.0.1",
},
},
},
},
UsedBy: []string{"foo@myprovider", "test@myprovider"},
}
si.UpdateServerStatus("http://127.0.0.1", "UP")
return si
}(),
},
},
expected: expected{
statusCode: http.StatusOK,
jsonFile: "testdata/service-foo-slash-bar.json",
},
},
{
desc: "one service by id, that does not exist",
path: "/api/http/services/nono@myprovider",
@ -897,6 +948,26 @@ func TestHandler_HTTP(t *testing.T) {
jsonFile: "testdata/middleware-auth.json",
},
},
{
desc: "one middleware by id containing slash",
path: "/api/http/middlewares/" + url.PathEscape("foo / bar@myprovider"),
conf: runtime.Configuration{
Middlewares: map[string]*runtime.MiddlewareInfo{
"foo / bar@myprovider": {
Middleware: &dynamic.Middleware{
AddPrefix: &dynamic.AddPrefix{
Prefix: "/titi",
},
},
UsedBy: []string{"test@myprovider"},
},
},
},
expected: expected{
statusCode: http.StatusOK,
jsonFile: "testdata/middleware-foo-slash-bar.json",
},
},
{
desc: "one middleware by id, that does not exist",
path: "/api/http/middlewares/foo@myprovider",

View file

@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
@ -90,7 +91,13 @@ func (h Handler) getTCPRouters(rw http.ResponseWriter, request *http.Request) {
}
func (h Handler) getTCPRouter(rw http.ResponseWriter, request *http.Request) {
routerID := mux.Vars(request)["routerID"]
scapedRouterID := mux.Vars(request)["routerID"]
routerID, err := url.PathUnescape(scapedRouterID)
if err != nil {
writeError(rw, fmt.Sprintf("unable to decode routerID %q: %s", scapedRouterID, err), http.StatusBadRequest)
return
}
rw.Header().Set("Content-Type", "application/json")
@ -102,7 +109,7 @@ func (h Handler) getTCPRouter(rw http.ResponseWriter, request *http.Request) {
result := newTCPRouterRepresentation(routerID, router)
err := json.NewEncoder(rw).Encode(result)
err = json.NewEncoder(rw).Encode(result)
if err != nil {
log.Ctx(request.Context()).Error().Err(err).Send()
writeError(rw, err.Error(), http.StatusInternalServerError)
@ -141,7 +148,13 @@ func (h Handler) getTCPServices(rw http.ResponseWriter, request *http.Request) {
}
func (h Handler) getTCPService(rw http.ResponseWriter, request *http.Request) {
serviceID := mux.Vars(request)["serviceID"]
scapedServiceID := mux.Vars(request)["serviceID"]
serviceID, err := url.PathUnescape(scapedServiceID)
if err != nil {
writeError(rw, fmt.Sprintf("unable to decode serviceID %q: %s", scapedServiceID, err), http.StatusBadRequest)
return
}
rw.Header().Set("Content-Type", "application/json")
@ -153,7 +166,7 @@ func (h Handler) getTCPService(rw http.ResponseWriter, request *http.Request) {
result := newTCPServiceRepresentation(serviceID, service)
err := json.NewEncoder(rw).Encode(result)
err = json.NewEncoder(rw).Encode(result)
if err != nil {
log.Ctx(request.Context()).Error().Err(err).Send()
writeError(rw, err.Error(), http.StatusInternalServerError)
@ -192,7 +205,13 @@ func (h Handler) getTCPMiddlewares(rw http.ResponseWriter, request *http.Request
}
func (h Handler) getTCPMiddleware(rw http.ResponseWriter, request *http.Request) {
middlewareID := mux.Vars(request)["middlewareID"]
scapedMiddlewareID := mux.Vars(request)["middlewareID"]
middlewareID, err := url.PathUnescape(scapedMiddlewareID)
if err != nil {
writeError(rw, fmt.Sprintf("unable to decode middlewareID %q: %s", scapedMiddlewareID, err), http.StatusBadRequest)
return
}
rw.Header().Set("Content-Type", "application/json")
@ -204,7 +223,7 @@ func (h Handler) getTCPMiddleware(rw http.ResponseWriter, request *http.Request)
result := newTCPMiddlewareRepresentation(middlewareID, middleware)
err := json.NewEncoder(rw).Encode(result)
err = json.NewEncoder(rw).Encode(result)
if err != nil {
log.Ctx(request.Context()).Error().Err(err).Send()
writeError(rw, err.Error(), http.StatusInternalServerError)

View file

@ -6,6 +6,7 @@ import (
"io"
"net/http"
"net/http/httptest"
"net/url"
"os"
"testing"
@ -295,6 +296,25 @@ func TestHandler_TCP(t *testing.T) {
jsonFile: "testdata/tcprouter-bar.json",
},
},
{
desc: "one TCP router by id containing slash",
path: "/api/tcp/routers/" + url.PathEscape("foo / bar@myprovider"),
conf: runtime.Configuration{
TCPRouters: map[string]*runtime.TCPRouterInfo{
"foo / bar@myprovider": {
TCPRouter: &dynamic.TCPRouter{
EntryPoints: []string{"web"},
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)",
},
},
},
},
expected: expected{
statusCode: http.StatusOK,
jsonFile: "testdata/tcprouter-foo-slash-bar.json",
},
},
{
desc: "one TCP router by id, that does not exist",
path: "/api/tcp/routers/foo@myprovider",
@ -559,6 +579,30 @@ func TestHandler_TCP(t *testing.T) {
jsonFile: "testdata/tcpservice-bar.json",
},
},
{
desc: "one tcp service by id containing slash",
path: "/api/tcp/services/" + url.PathEscape("foo / bar@myprovider"),
conf: runtime.Configuration{
TCPServices: map[string]*runtime.TCPServiceInfo{
"foo / bar@myprovider": {
TCPService: &dynamic.TCPService{
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "127.0.0.1:2345",
},
},
},
},
UsedBy: []string{"foo@myprovider", "test@myprovider"},
},
},
},
expected: expected{
statusCode: http.StatusOK,
jsonFile: "testdata/tcpservice-foo-slash-bar.json",
},
},
{
desc: "one tcp service by id, that does not exist",
path: "/api/tcp/services/nono@myprovider",
@ -780,6 +824,26 @@ func TestHandler_TCP(t *testing.T) {
jsonFile: "testdata/tcpmiddleware-ipallowlist.json",
},
},
{
desc: "one middleware by id containing slash",
path: "/api/tcp/middlewares/" + url.PathEscape("foo / bar@myprovider"),
conf: runtime.Configuration{
TCPMiddlewares: map[string]*runtime.TCPMiddlewareInfo{
"foo / bar@myprovider": {
TCPMiddleware: &dynamic.TCPMiddleware{
IPWhiteList: &dynamic.TCPIPWhiteList{
SourceRange: []string{"127.0.0.1/32"},
},
},
UsedBy: []string{"bar@myprovider", "test@myprovider"},
},
},
},
expected: expected{
statusCode: http.StatusOK,
jsonFile: "testdata/tcpmiddleware-foo-slash-bar.json",
},
},
{
desc: "one middleware by id, that does not exist",
path: "/api/tcp/middlewares/foo@myprovider",

View file

@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
@ -74,7 +75,13 @@ func (h Handler) getUDPRouters(rw http.ResponseWriter, request *http.Request) {
}
func (h Handler) getUDPRouter(rw http.ResponseWriter, request *http.Request) {
routerID := mux.Vars(request)["routerID"]
scapedRouterID := mux.Vars(request)["routerID"]
routerID, err := url.PathUnescape(scapedRouterID)
if err != nil {
writeError(rw, fmt.Sprintf("unable to decode routerID %q: %s", scapedRouterID, err), http.StatusBadRequest)
return
}
rw.Header().Set("Content-Type", "application/json")
@ -86,7 +93,7 @@ func (h Handler) getUDPRouter(rw http.ResponseWriter, request *http.Request) {
result := newUDPRouterRepresentation(routerID, router)
err := json.NewEncoder(rw).Encode(result)
err = json.NewEncoder(rw).Encode(result)
if err != nil {
log.Ctx(request.Context()).Error().Err(err).Send()
writeError(rw, err.Error(), http.StatusInternalServerError)
@ -125,7 +132,13 @@ func (h Handler) getUDPServices(rw http.ResponseWriter, request *http.Request) {
}
func (h Handler) getUDPService(rw http.ResponseWriter, request *http.Request) {
serviceID := mux.Vars(request)["serviceID"]
scapedServiceID := mux.Vars(request)["serviceID"]
serviceID, err := url.PathUnescape(scapedServiceID)
if err != nil {
writeError(rw, fmt.Sprintf("unable to decode serviceID %q: %s", scapedServiceID, err), http.StatusBadRequest)
return
}
rw.Header().Set("Content-Type", "application/json")
@ -137,7 +150,7 @@ func (h Handler) getUDPService(rw http.ResponseWriter, request *http.Request) {
result := newUDPServiceRepresentation(serviceID, service)
err := json.NewEncoder(rw).Encode(result)
err = json.NewEncoder(rw).Encode(result)
if err != nil {
log.Ctx(request.Context()).Error().Err(err).Send()
writeError(rw, err.Error(), http.StatusInternalServerError)

View file

@ -6,6 +6,7 @@ import (
"io"
"net/http"
"net/http/httptest"
"net/url"
"os"
"testing"
@ -224,6 +225,24 @@ func TestHandler_UDP(t *testing.T) {
jsonFile: "testdata/udprouter-bar.json",
},
},
{
desc: "one UDP router by id containing slash",
path: "/api/udp/routers/" + url.PathEscape("foo / bar@myprovider"),
conf: runtime.Configuration{
UDPRouters: map[string]*runtime.UDPRouterInfo{
"foo / bar@myprovider": {
UDPRouter: &dynamic.UDPRouter{
EntryPoints: []string{"web"},
Service: "foo-service@myprovider",
},
},
},
},
expected: expected{
statusCode: http.StatusOK,
jsonFile: "testdata/udprouter-foo-slash-bar.json",
},
},
{
desc: "one UDP router by id, that does not exist",
path: "/api/udp/routers/foo@myprovider",
@ -487,6 +506,30 @@ func TestHandler_UDP(t *testing.T) {
jsonFile: "testdata/udpservice-bar.json",
},
},
{
desc: "one udp service by id containing slash",
path: "/api/udp/services/" + url.PathEscape("foo / bar@myprovider"),
conf: runtime.Configuration{
UDPServices: map[string]*runtime.UDPServiceInfo{
"foo / bar@myprovider": {
UDPService: &dynamic.UDPService{
LoadBalancer: &dynamic.UDPServersLoadBalancer{
Servers: []dynamic.UDPServer{
{
Address: "127.0.0.1:2345",
},
},
},
},
UsedBy: []string{"foo@myprovider", "test@myprovider"},
},
},
},
expected: expected{
statusCode: http.StatusOK,
jsonFile: "testdata/udpservice-foo-slash-bar.json",
},
},
{
desc: "one udp service by id, that does not exist",
path: "/api/udp/services/nono@myprovider",

View file

@ -0,0 +1,5 @@
{
"address": ":81",
"http": {},
"name": "foo / bar"
}

View file

@ -0,0 +1,12 @@
{
"addPrefix": {
"prefix": "/titi"
},
"name": "foo / bar@myprovider",
"provider": "myprovider",
"status": "enabled",
"type": "addprefix",
"usedBy": [
"test@myprovider"
]
}

View file

@ -0,0 +1,17 @@
{
"entryPoints": [
"web"
],
"middlewares": [
"auth",
"addPrefixTest@anotherprovider"
],
"name": "foo / bar@myprovider",
"provider": "myprovider",
"rule": "Host(`foo.bar`)",
"service": "foo-service@myprovider",
"status": "enabled",
"using": [
"web"
]
}

View file

@ -0,0 +1,21 @@
{
"loadBalancer": {
"passHostHeader": true,
"servers": [
{
"url": "http://127.0.0.1"
}
]
},
"name": "foo / bar@myprovider",
"provider": "myprovider",
"serverStatus": {
"http://127.0.0.1": "UP"
},
"status": "enabled",
"type": "loadbalancer",
"usedBy": [
"foo@myprovider",
"test@myprovider"
]
}

View file

@ -0,0 +1,13 @@
{
"ipWhiteList": {
"sourceRange": ["127.0.0.1/32"]
},
"name": "foo / bar@myprovider",
"provider": "myprovider",
"status": "enabled",
"type": "ipwhitelist",
"usedBy": [
"bar@myprovider",
"test@myprovider"
]
}

View file

@ -0,0 +1,13 @@
{
"entryPoints": [
"web"
],
"name": "foo / bar@myprovider",
"provider": "myprovider",
"rule": "Host(`foo.bar`)",
"service": "foo-service@myprovider",
"status": "enabled",
"using": [
"web"
]
}

View file

@ -0,0 +1,17 @@
{
"loadBalancer": {
"servers": [
{
"address": "127.0.0.1:2345"
}
]
},
"name": "foo / bar@myprovider",
"provider": "myprovider",
"status": "enabled",
"type": "loadbalancer",
"usedBy": [
"foo@myprovider",
"test@myprovider"
]
}

View file

@ -0,0 +1,12 @@
{
"entryPoints": [
"web"
],
"name": "foo / bar@myprovider",
"provider": "myprovider",
"service": "foo-service@myprovider",
"status": "enabled",
"using": [
"web"
]
}

View file

@ -0,0 +1,17 @@
{
"loadBalancer": {
"servers": [
{
"address": "127.0.0.1:2345"
}
]
},
"name": "foo / bar@myprovider",
"provider": "myprovider",
"status": "enabled",
"type": "loadbalancer",
"usedBy": [
"foo@myprovider",
"test@myprovider"
]
}