Add TCP Middlewares support

This commit is contained in:
Romain 2021-06-11 15:30:05 +02:00 committed by GitHub
parent 679def0151
commit fc9f41b955
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
134 changed files with 5865 additions and 1852 deletions

View file

@ -36,13 +36,14 @@ type serviceInfoRepresentation struct {
// RunTimeRepresentation is the configuration information exposed by the API handler.
type RunTimeRepresentation struct {
Routers map[string]*runtime.RouterInfo `json:"routers,omitempty"`
Middlewares map[string]*runtime.MiddlewareInfo `json:"middlewares,omitempty"`
Services map[string]*serviceInfoRepresentation `json:"services,omitempty"`
TCPRouters map[string]*runtime.TCPRouterInfo `json:"tcpRouters,omitempty"`
TCPServices map[string]*runtime.TCPServiceInfo `json:"tcpServices,omitempty"`
UDPRouters map[string]*runtime.UDPRouterInfo `json:"udpRouters,omitempty"`
UDPServices map[string]*runtime.UDPServiceInfo `json:"udpServices,omitempty"`
Routers map[string]*runtime.RouterInfo `json:"routers,omitempty"`
Middlewares map[string]*runtime.MiddlewareInfo `json:"middlewares,omitempty"`
Services map[string]*serviceInfoRepresentation `json:"services,omitempty"`
TCPRouters map[string]*runtime.TCPRouterInfo `json:"tcpRouters,omitempty"`
TCPMiddlewares map[string]*runtime.TCPMiddlewareInfo `json:"tcpMiddlewares,omitempty"`
TCPServices map[string]*runtime.TCPServiceInfo `json:"tcpServices,omitempty"`
UDPRouters map[string]*runtime.UDPRouterInfo `json:"udpRouters,omitempty"`
UDPServices map[string]*runtime.UDPServiceInfo `json:"udpServices,omitempty"`
}
// Handler serves the configuration and status of Traefik on API endpoints.
@ -107,6 +108,8 @@ func (h Handler) createRouter() *mux.Router {
router.Methods(http.MethodGet).Path("/api/tcp/routers/{routerID}").HandlerFunc(h.getTCPRouter)
router.Methods(http.MethodGet).Path("/api/tcp/services").HandlerFunc(h.getTCPServices)
router.Methods(http.MethodGet).Path("/api/tcp/services/{serviceID}").HandlerFunc(h.getTCPService)
router.Methods(http.MethodGet).Path("/api/tcp/middlewares").HandlerFunc(h.getTCPMiddlewares)
router.Methods(http.MethodGet).Path("/api/tcp/middlewares/{middlewareID}").HandlerFunc(h.getTCPMiddleware)
router.Methods(http.MethodGet).Path("/api/udp/routers").HandlerFunc(h.getUDPRouters)
router.Methods(http.MethodGet).Path("/api/udp/routers/{routerID}").HandlerFunc(h.getUDPRouter)
@ -132,13 +135,14 @@ func (h Handler) getRuntimeConfiguration(rw http.ResponseWriter, request *http.R
}
result := RunTimeRepresentation{
Routers: h.runtimeConfiguration.Routers,
Middlewares: h.runtimeConfiguration.Middlewares,
Services: siRepr,
TCPRouters: h.runtimeConfiguration.TCPRouters,
TCPServices: h.runtimeConfiguration.TCPServices,
UDPRouters: h.runtimeConfiguration.UDPRouters,
UDPServices: h.runtimeConfiguration.UDPServices,
Routers: h.runtimeConfiguration.Routers,
Middlewares: h.runtimeConfiguration.Middlewares,
Services: siRepr,
TCPRouters: h.runtimeConfiguration.TCPRouters,
TCPMiddlewares: h.runtimeConfiguration.TCPMiddlewares,
TCPServices: h.runtimeConfiguration.TCPServices,
UDPRouters: h.runtimeConfiguration.UDPRouters,
UDPServices: h.runtimeConfiguration.UDPServices,
}
rw.Header().Set("Content-Type", "application/json")

View file

@ -45,8 +45,9 @@ func (h Handler) getOverview(rw http.ResponseWriter, request *http.Request) {
Middlewares: getHTTPMiddlewareSection(h.runtimeConfiguration.Middlewares),
},
TCP: schemeOverview{
Routers: getTCPRouterSection(h.runtimeConfiguration.TCPRouters),
Services: getTCPServiceSection(h.runtimeConfiguration.TCPServices),
Routers: getTCPRouterSection(h.runtimeConfiguration.TCPRouters),
Services: getTCPServiceSection(h.runtimeConfiguration.TCPServices),
Middlewares: getTCPMiddlewareSection(h.runtimeConfiguration.TCPMiddlewares),
},
UDP: schemeOverview{
Routers: getUDPRouterSection(h.runtimeConfiguration.UDPRouters),
@ -160,6 +161,25 @@ func getTCPServiceSection(services map[string]*runtime.TCPServiceInfo) *section
}
}
func getTCPMiddlewareSection(middlewares map[string]*runtime.TCPMiddlewareInfo) *section {
var countErrors int
var countWarnings int
for _, mid := range middlewares {
switch mid.Status {
case runtime.StatusDisabled:
countErrors++
case runtime.StatusWarning:
countWarnings++
}
}
return &section{
Total: len(middlewares),
Warnings: countWarnings,
Errors: countErrors,
}
}
func getUDPRouterSection(routers map[string]*runtime.UDPRouterInfo) *section {
var countErrors int
var countWarnings int

View file

@ -171,6 +171,31 @@ func TestHandler_Overview(t *testing.T) {
Status: runtime.StatusDisabled,
},
},
TCPMiddlewares: map[string]*runtime.TCPMiddlewareInfo{
"ipwhitelist1@myprovider": {
TCPMiddleware: &dynamic.TCPMiddleware{
IPWhiteList: &dynamic.TCPIPWhiteList{
SourceRange: []string{"127.0.0.1/32"},
},
},
Status: runtime.StatusEnabled,
},
"ipwhitelist2@myprovider": {
TCPMiddleware: &dynamic.TCPMiddleware{
IPWhiteList: &dynamic.TCPIPWhiteList{
SourceRange: []string{"127.0.0.1/32"},
},
},
},
"ipwhitelist3@myprovider": {
TCPMiddleware: &dynamic.TCPMiddleware{
IPWhiteList: &dynamic.TCPIPWhiteList{
SourceRange: []string{"127.0.0.1/32"},
},
},
Status: runtime.StatusDisabled,
},
},
TCPRouters: map[string]*runtime.TCPRouterInfo{
"tcpbar@myprovider": {
TCPRouter: &dynamic.TCPRouter{

View file

@ -43,6 +43,22 @@ func newTCPServiceRepresentation(name string, si *runtime.TCPServiceInfo) tcpSer
}
}
type tcpMiddlewareRepresentation struct {
*runtime.TCPMiddlewareInfo
Name string `json:"name,omitempty"`
Provider string `json:"provider,omitempty"`
Type string `json:"type,omitempty"`
}
func newTCPMiddlewareRepresentation(name string, mi *runtime.TCPMiddlewareInfo) tcpMiddlewareRepresentation {
return tcpMiddlewareRepresentation{
TCPMiddlewareInfo: mi,
Name: name,
Provider: getProviderName(name),
Type: strings.ToLower(extractType(mi.TCPMiddleware)),
}
}
func (h Handler) getTCPRouters(rw http.ResponseWriter, request *http.Request) {
results := make([]tcpRouterRepresentation, 0, len(h.runtimeConfiguration.TCPRouters))
@ -147,6 +163,58 @@ func (h Handler) getTCPService(rw http.ResponseWriter, request *http.Request) {
}
}
func (h Handler) getTCPMiddlewares(rw http.ResponseWriter, request *http.Request) {
results := make([]tcpMiddlewareRepresentation, 0, len(h.runtimeConfiguration.Middlewares))
criterion := newSearchCriterion(request.URL.Query())
for name, mi := range h.runtimeConfiguration.TCPMiddlewares {
if keepTCPMiddleware(name, mi, criterion) {
results = append(results, newTCPMiddlewareRepresentation(name, mi))
}
}
sort.Slice(results, func(i, j int) bool {
return results[i].Name < results[j].Name
})
rw.Header().Set("Content-Type", "application/json")
pageInfo, err := pagination(request, len(results))
if err != nil {
writeError(rw, err.Error(), http.StatusBadRequest)
return
}
rw.Header().Set(nextPageHeader, strconv.Itoa(pageInfo.nextPage))
err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
if err != nil {
log.FromContext(request.Context()).Error(err)
writeError(rw, err.Error(), http.StatusInternalServerError)
}
}
func (h Handler) getTCPMiddleware(rw http.ResponseWriter, request *http.Request) {
middlewareID := mux.Vars(request)["middlewareID"]
rw.Header().Set("Content-Type", "application/json")
middleware, ok := h.runtimeConfiguration.TCPMiddlewares[middlewareID]
if !ok {
writeError(rw, fmt.Sprintf("middleware not found: %s", middlewareID), http.StatusNotFound)
return
}
result := newTCPMiddlewareRepresentation(middlewareID, middleware)
err := json.NewEncoder(rw).Encode(result)
if err != nil {
log.FromContext(request.Context()).Error(err)
writeError(rw, err.Error(), http.StatusInternalServerError)
}
}
func keepTCPRouter(name string, item *runtime.TCPRouterInfo, criterion *searchCriterion) bool {
if criterion == nil {
return true
@ -162,3 +230,11 @@ func keepTCPService(name string, item *runtime.TCPServiceInfo, criterion *search
return criterion.withStatus(item.Status) && criterion.searchIn(name)
}
func keepTCPMiddleware(name string, item *runtime.TCPMiddlewareInfo, criterion *searchCriterion) bool {
if criterion == nil {
return true
}
return criterion.withStatus(item.Status) && criterion.searchIn(name)
}

View file

@ -507,6 +507,223 @@ func TestHandler_TCP(t *testing.T) {
statusCode: http.StatusNotFound,
},
},
{
desc: "all middlewares",
path: "/api/tcp/middlewares",
conf: runtime.Configuration{
TCPMiddlewares: map[string]*runtime.TCPMiddlewareInfo{
"ipwhitelist1@myprovider": {
TCPMiddleware: &dynamic.TCPMiddleware{
IPWhiteList: &dynamic.TCPIPWhiteList{
SourceRange: []string{"127.0.0.1/32"},
},
},
UsedBy: []string{"bar@myprovider", "test@myprovider"},
},
"ipwhitelist2@myprovider": {
TCPMiddleware: &dynamic.TCPMiddleware{
IPWhiteList: &dynamic.TCPIPWhiteList{
SourceRange: []string{"127.0.0.2/32"},
},
},
UsedBy: []string{"test@myprovider"},
},
"ipwhitelist1@anotherprovider": {
TCPMiddleware: &dynamic.TCPMiddleware{
IPWhiteList: &dynamic.TCPIPWhiteList{
SourceRange: []string{"127.0.0.1/32"},
},
},
UsedBy: []string{"bar@myprovider"},
},
},
},
expected: expected{
statusCode: http.StatusOK,
nextPage: "1",
jsonFile: "testdata/tcpmiddlewares.json",
},
},
{
desc: "middlewares filtered by status",
path: "/api/tcp/middlewares?status=enabled",
conf: runtime.Configuration{
TCPMiddlewares: map[string]*runtime.TCPMiddlewareInfo{
"ipwhitelist@myprovider": {
TCPMiddleware: &dynamic.TCPMiddleware{
IPWhiteList: &dynamic.TCPIPWhiteList{
SourceRange: []string{"127.0.0.1/32"},
},
},
UsedBy: []string{"bar@myprovider", "test@myprovider"},
Status: runtime.StatusEnabled,
},
"ipwhitelist2@myprovider": {
TCPMiddleware: &dynamic.TCPMiddleware{
IPWhiteList: &dynamic.TCPIPWhiteList{
SourceRange: []string{"127.0.0.2/32"},
},
},
UsedBy: []string{"test@myprovider"},
Status: runtime.StatusDisabled,
},
"ipwhitelist@anotherprovider": {
TCPMiddleware: &dynamic.TCPMiddleware{
IPWhiteList: &dynamic.TCPIPWhiteList{
SourceRange: []string{"127.0.0.1/32"},
},
},
UsedBy: []string{"bar@myprovider"},
Status: runtime.StatusEnabled,
},
},
},
expected: expected{
statusCode: http.StatusOK,
nextPage: "1",
jsonFile: "testdata/tcpmiddlewares-filtered-status.json",
},
},
{
desc: "middlewares filtered by search",
path: "/api/tcp/middlewares?search=ipwhitelist",
conf: runtime.Configuration{
TCPMiddlewares: map[string]*runtime.TCPMiddlewareInfo{
"bad@myprovider": {
TCPMiddleware: &dynamic.TCPMiddleware{
IPWhiteList: &dynamic.TCPIPWhiteList{
SourceRange: []string{"127.0.0.1/32"},
},
},
UsedBy: []string{"bar@myprovider", "test@myprovider"},
Status: runtime.StatusEnabled,
},
"ipwhitelist@myprovider": {
TCPMiddleware: &dynamic.TCPMiddleware{
IPWhiteList: &dynamic.TCPIPWhiteList{
SourceRange: []string{"127.0.0.1/32"},
},
},
UsedBy: []string{"test@myprovider"},
Status: runtime.StatusDisabled,
},
"ipwhitelist@anotherprovider": {
TCPMiddleware: &dynamic.TCPMiddleware{
IPWhiteList: &dynamic.TCPIPWhiteList{
SourceRange: []string{"127.0.0.1/32"},
},
},
UsedBy: []string{"bar@myprovider"},
Status: runtime.StatusEnabled,
},
},
},
expected: expected{
statusCode: http.StatusOK,
nextPage: "1",
jsonFile: "testdata/tcpmiddlewares-filtered-search.json",
},
},
{
desc: "all middlewares, 1 res per page, want page 2",
path: "/api/tcp/middlewares?page=2&per_page=1",
conf: runtime.Configuration{
TCPMiddlewares: map[string]*runtime.TCPMiddlewareInfo{
"ipwhitelist@myprovider": {
TCPMiddleware: &dynamic.TCPMiddleware{
IPWhiteList: &dynamic.TCPIPWhiteList{
SourceRange: []string{"127.0.0.1/32"},
},
},
UsedBy: []string{"bar@myprovider", "test@myprovider"},
},
"ipwhitelist2@myprovider": {
TCPMiddleware: &dynamic.TCPMiddleware{
IPWhiteList: &dynamic.TCPIPWhiteList{
SourceRange: []string{"127.0.0.2/32"},
},
},
UsedBy: []string{"test@myprovider"},
},
"ipwhitelist@anotherprovider": {
TCPMiddleware: &dynamic.TCPMiddleware{
IPWhiteList: &dynamic.TCPIPWhiteList{
SourceRange: []string{"127.0.0.1/32"},
},
},
UsedBy: []string{"bar@myprovider"},
},
},
},
expected: expected{
statusCode: http.StatusOK,
nextPage: "3",
jsonFile: "testdata/tcpmiddlewares-page2.json",
},
},
{
desc: "one middleware by id",
path: "/api/tcp/middlewares/ipwhitelist@myprovider",
conf: runtime.Configuration{
TCPMiddlewares: map[string]*runtime.TCPMiddlewareInfo{
"ipwhitelist@myprovider": {
TCPMiddleware: &dynamic.TCPMiddleware{
IPWhiteList: &dynamic.TCPIPWhiteList{
SourceRange: []string{"127.0.0.1/32"},
},
},
UsedBy: []string{"bar@myprovider", "test@myprovider"},
},
"ipwhitelist2@myprovider": {
TCPMiddleware: &dynamic.TCPMiddleware{
IPWhiteList: &dynamic.TCPIPWhiteList{
SourceRange: []string{"127.0.0.2/32"},
},
},
UsedBy: []string{"test@myprovider"},
},
"ipwhitelist@anotherprovider": {
TCPMiddleware: &dynamic.TCPMiddleware{
IPWhiteList: &dynamic.TCPIPWhiteList{
SourceRange: []string{"127.0.0.1/32"},
},
},
UsedBy: []string{"bar@myprovider"},
},
},
},
expected: expected{
statusCode: http.StatusOK,
jsonFile: "testdata/tcpmiddleware-ipwhitelist.json",
},
},
{
desc: "one middleware by id, that does not exist",
path: "/api/tcp/middlewares/foo@myprovider",
conf: runtime.Configuration{
TCPMiddlewares: map[string]*runtime.TCPMiddlewareInfo{
"ipwhitelist@myprovider": {
TCPMiddleware: &dynamic.TCPMiddleware{
IPWhiteList: &dynamic.TCPIPWhiteList{
SourceRange: []string{"127.0.0.1/32"},
},
},
UsedBy: []string{"bar@myprovider", "test@myprovider"},
},
},
},
expected: expected{
statusCode: http.StatusNotFound,
},
},
{
desc: "one middleware by id, but no config",
path: "/api/tcp/middlewares/foo@myprovider",
conf: runtime.Configuration{},
expected: expected{
statusCode: http.StatusNotFound,
},
},
}
for _, test := range testCases {

View file

@ -22,6 +22,11 @@
}
},
"tcp": {
"middlewares": {
"errors": 1,
"total": 3,
"warnings": 0
},
"routers": {
"errors": 1,
"total": 3,

View file

@ -22,6 +22,11 @@
}
},
"tcp": {
"middlewares": {
"errors": 0,
"total": 0,
"warnings": 0
},
"routers": {
"errors": 0,
"total": 0,

View file

@ -22,6 +22,11 @@
}
},
"tcp": {
"middlewares": {
"errors": 0,
"total": 0,
"warnings": 0
},
"routers": {
"errors": 0,
"total": 0,

View file

@ -32,6 +32,11 @@
"plugin-test"
],
"tcp": {
"middlewares": {
"errors": 0,
"total": 0,
"warnings": 0
},
"routers": {
"errors": 0,
"total": 0,

View file

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

View file

@ -0,0 +1 @@
[]

View file

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

View file

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

View file

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

39
pkg/api/testdata/tcpmiddlewares.json vendored Normal file
View file

@ -0,0 +1,39 @@
[
{
"ipWhiteList": {
"sourceRange": ["127.0.0.1/32"]
},
"name": "ipwhitelist1@anotherprovider",
"provider": "anotherprovider",
"status": "enabled",
"type": "ipwhitelist",
"usedBy": [
"bar@myprovider"
]
},
{
"ipWhiteList": {
"sourceRange": ["127.0.0.1/32"]
},
"name": "ipwhitelist1@myprovider",
"provider": "myprovider",
"status": "enabled",
"type": "ipwhitelist",
"usedBy": [
"bar@myprovider",
"test@myprovider"
]
},
{
"ipWhiteList": {
"sourceRange": ["127.0.0.2/32"]
},
"name": "ipwhitelist2@myprovider",
"provider": "myprovider",
"status": "enabled",
"type": "ipwhitelist",
"usedBy": [
"test@myprovider"
]
}
]