Improve API for the web UI
This commit is contained in:
parent
17554202f6
commit
af9762cf32
41 changed files with 1200 additions and 199 deletions
35
integration/testdata/rawdata-crd.json
vendored
35
integration/testdata/rawdata-crd.json
vendored
|
@ -10,7 +10,10 @@
|
|||
"tls": {
|
||||
"options": "default/mytlsoption"
|
||||
},
|
||||
"status": "enabled"
|
||||
"status": "enabled",
|
||||
"using": [
|
||||
"web"
|
||||
]
|
||||
},
|
||||
"default/test2.route-23c7f4c450289ee29016@kubernetescrd": {
|
||||
"entryPoints": [
|
||||
|
@ -21,7 +24,10 @@
|
|||
],
|
||||
"service": "default/test2.route-23c7f4c450289ee29016",
|
||||
"rule": "Host(`foo.com`) \u0026\u0026 PathPrefix(`/tobestripped`)",
|
||||
"status": "enabled"
|
||||
"status": "enabled",
|
||||
"using": [
|
||||
"web"
|
||||
]
|
||||
}
|
||||
},
|
||||
"middlewares": {
|
||||
|
@ -42,10 +48,10 @@
|
|||
"loadBalancer": {
|
||||
"servers": [
|
||||
{
|
||||
"url": "http://10.42.0.4:80"
|
||||
"url": "http://10.42.0.2:80"
|
||||
},
|
||||
{
|
||||
"url": "http://10.42.0.5:80"
|
||||
"url": "http://10.42.0.6:80"
|
||||
}
|
||||
],
|
||||
"passHostHeader": true
|
||||
|
@ -55,18 +61,18 @@
|
|||
"default/test.route-6b204d94623b3df4370c@kubernetescrd"
|
||||
],
|
||||
"serverStatus": {
|
||||
"http://10.42.0.4:80": "UP",
|
||||
"http://10.42.0.5:80": "UP"
|
||||
"http://10.42.0.2:80": "UP",
|
||||
"http://10.42.0.6:80": "UP"
|
||||
}
|
||||
},
|
||||
"default/test2.route-23c7f4c450289ee29016@kubernetescrd": {
|
||||
"loadBalancer": {
|
||||
"servers": [
|
||||
{
|
||||
"url": "http://10.42.0.4:80"
|
||||
"url": "http://10.42.0.2:80"
|
||||
},
|
||||
{
|
||||
"url": "http://10.42.0.5:80"
|
||||
"url": "http://10.42.0.6:80"
|
||||
}
|
||||
],
|
||||
"passHostHeader": true
|
||||
|
@ -76,8 +82,8 @@
|
|||
"default/test2.route-23c7f4c450289ee29016@kubernetescrd"
|
||||
],
|
||||
"serverStatus": {
|
||||
"http://10.42.0.4:80": "UP",
|
||||
"http://10.42.0.5:80": "UP"
|
||||
"http://10.42.0.2:80": "UP",
|
||||
"http://10.42.0.6:80": "UP"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -92,7 +98,10 @@
|
|||
"passthrough": false,
|
||||
"options": "default/mytlsoption"
|
||||
},
|
||||
"status": "enabled"
|
||||
"status": "enabled",
|
||||
"using": [
|
||||
"footcp"
|
||||
]
|
||||
}
|
||||
},
|
||||
"tcpServices": {
|
||||
|
@ -100,10 +109,10 @@
|
|||
"loadBalancer": {
|
||||
"servers": [
|
||||
{
|
||||
"address": "10.42.0.3:8080"
|
||||
"address": "10.42.0.4:8080"
|
||||
},
|
||||
{
|
||||
"address": "10.42.0.6:8080"
|
||||
"address": "10.42.0.5:8080"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
26
integration/testdata/rawdata-ingress.json
vendored
26
integration/testdata/rawdata-ingress.json
vendored
|
@ -4,17 +4,29 @@
|
|||
"service": "default/whoami/http",
|
||||
"rule": "Host(`whoami.test.https`) \u0026\u0026 PathPrefix(`/whoami`)",
|
||||
"tls": {},
|
||||
"status": "enabled"
|
||||
"status": "enabled",
|
||||
"using": [
|
||||
"traefik",
|
||||
"web"
|
||||
]
|
||||
},
|
||||
"whoami-test-https/whoami@kubernetes": {
|
||||
"service": "default/whoami/http",
|
||||
"rule": "Host(`whoami.test.https`) \u0026\u0026 PathPrefix(`/whoami`)",
|
||||
"status": "enabled"
|
||||
"status": "enabled",
|
||||
"using": [
|
||||
"traefik",
|
||||
"web"
|
||||
]
|
||||
},
|
||||
"whoami-test/whoami@kubernetes": {
|
||||
"service": "default/whoami/http",
|
||||
"rule": "Host(`whoami.test`) \u0026\u0026 PathPrefix(`/whoami`)",
|
||||
"status": "enabled"
|
||||
"status": "enabled",
|
||||
"using": [
|
||||
"traefik",
|
||||
"web"
|
||||
]
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
|
@ -22,10 +34,10 @@
|
|||
"loadBalancer": {
|
||||
"servers": [
|
||||
{
|
||||
"url": "http://10.42.0.4:80"
|
||||
"url": "http://10.42.0.2:80"
|
||||
},
|
||||
{
|
||||
"url": "http://10.42.0.5:80"
|
||||
"url": "http://10.42.0.4:80"
|
||||
}
|
||||
],
|
||||
"passHostHeader": true
|
||||
|
@ -37,8 +49,8 @@
|
|||
"whoami-test/whoami@kubernetes"
|
||||
],
|
||||
"serverStatus": {
|
||||
"http://10.42.0.4:80": "UP",
|
||||
"http://10.42.0.5:80": "UP"
|
||||
"http://10.42.0.2:80": "UP",
|
||||
"http://10.42.0.4:80": "UP"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
102
pkg/api/criterion.go
Normal file
102
pkg/api/criterion.go
Normal file
|
@ -0,0 +1,102 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultPerPage = 100
|
||||
defaultPage = 1
|
||||
)
|
||||
|
||||
const nextPageHeader = "X-Next-Page"
|
||||
|
||||
type pageInfo struct {
|
||||
startIndex int
|
||||
endIndex int
|
||||
nextPage int
|
||||
}
|
||||
|
||||
type searchCriterion struct {
|
||||
Search string `url:"search"`
|
||||
Status string `url:"status"`
|
||||
}
|
||||
|
||||
func newSearchCriterion(query url.Values) *searchCriterion {
|
||||
if len(query) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
search := query.Get("search")
|
||||
status := query.Get("status")
|
||||
|
||||
if status == "" && search == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &searchCriterion{Search: search, Status: status}
|
||||
}
|
||||
|
||||
func (c *searchCriterion) withStatus(name string) bool {
|
||||
return c.Status == "" || strings.EqualFold(name, c.Status)
|
||||
}
|
||||
|
||||
func (c *searchCriterion) searchIn(values ...string) bool {
|
||||
if c.Search == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, v := range values {
|
||||
if strings.Contains(strings.ToLower(v), strings.ToLower(c.Search)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func pagination(request *http.Request, max int) (pageInfo, error) {
|
||||
perPage, err := getIntParam(request, "per_page", defaultPerPage)
|
||||
if err != nil {
|
||||
return pageInfo{}, err
|
||||
}
|
||||
|
||||
page, err := getIntParam(request, "page", defaultPage)
|
||||
if err != nil {
|
||||
return pageInfo{}, err
|
||||
}
|
||||
|
||||
startIndex := (page - 1) * perPage
|
||||
if startIndex != 0 && startIndex >= max {
|
||||
return pageInfo{}, fmt.Errorf("invalid request: page: %d, per_page: %d", page, perPage)
|
||||
}
|
||||
|
||||
endIndex := startIndex + perPage
|
||||
if endIndex >= max {
|
||||
endIndex = max
|
||||
}
|
||||
|
||||
nextPage := 1
|
||||
if page*perPage < max {
|
||||
nextPage = page + 1
|
||||
}
|
||||
|
||||
return pageInfo{startIndex: startIndex, endIndex: endIndex, nextPage: nextPage}, nil
|
||||
}
|
||||
|
||||
func getIntParam(request *http.Request, key string, defaultValue int) (int, error) {
|
||||
raw := request.URL.Query().Get(key)
|
||||
if raw == "" {
|
||||
return defaultValue, nil
|
||||
}
|
||||
|
||||
value, err := strconv.Atoi(raw)
|
||||
if err != nil || value <= 0 {
|
||||
return 0, fmt.Errorf("invalid request: %s: %d", key, value)
|
||||
}
|
||||
return value, nil
|
||||
}
|
|
@ -2,9 +2,8 @@ package api
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/containous/traefik/v2/pkg/config/runtime"
|
||||
|
@ -15,12 +14,19 @@ import (
|
|||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultPerPage = 100
|
||||
defaultPage = 1
|
||||
)
|
||||
type apiError struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
const nextPageHeader = "X-Next-Page"
|
||||
func writeError(rw http.ResponseWriter, msg string, code int) {
|
||||
data, err := json.Marshal(apiError{Message: msg})
|
||||
if err != nil {
|
||||
http.Error(rw, msg, code)
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(rw, string(data), code)
|
||||
}
|
||||
|
||||
type serviceInfoRepresentation struct {
|
||||
*runtime.ServiceInfo
|
||||
|
@ -36,12 +42,6 @@ type RunTimeRepresentation struct {
|
|||
TCPServices map[string]*runtime.TCPServiceInfo `json:"tcpServices,omitempty"`
|
||||
}
|
||||
|
||||
type pageInfo struct {
|
||||
startIndex int
|
||||
endIndex int
|
||||
nextPage int
|
||||
}
|
||||
|
||||
// Handler serves the configuration and status of Traefik on API endpoints.
|
||||
type Handler struct {
|
||||
dashboard bool
|
||||
|
@ -136,48 +136,19 @@ func (h Handler) getRuntimeConfiguration(rw http.ResponseWriter, request *http.R
|
|||
}
|
||||
}
|
||||
|
||||
func pagination(request *http.Request, max int) (pageInfo, error) {
|
||||
perPage, err := getIntParam(request, "per_page", defaultPerPage)
|
||||
if err != nil {
|
||||
return pageInfo{}, err
|
||||
}
|
||||
|
||||
page, err := getIntParam(request, "page", defaultPage)
|
||||
if err != nil {
|
||||
return pageInfo{}, err
|
||||
}
|
||||
|
||||
startIndex := (page - 1) * perPage
|
||||
if startIndex != 0 && startIndex >= max {
|
||||
return pageInfo{}, fmt.Errorf("invalid request: page: %d, per_page: %d", page, perPage)
|
||||
}
|
||||
|
||||
endIndex := startIndex + perPage
|
||||
if endIndex >= max {
|
||||
endIndex = max
|
||||
}
|
||||
|
||||
nextPage := 1
|
||||
if page*perPage < max {
|
||||
nextPage = page + 1
|
||||
}
|
||||
|
||||
return pageInfo{startIndex: startIndex, endIndex: endIndex, nextPage: nextPage}, nil
|
||||
}
|
||||
|
||||
func getIntParam(request *http.Request, key string, defaultValue int) (int, error) {
|
||||
raw := request.URL.Query().Get(key)
|
||||
if raw == "" {
|
||||
return defaultValue, nil
|
||||
}
|
||||
|
||||
value, err := strconv.Atoi(raw)
|
||||
if err != nil || value <= 0 {
|
||||
return 0, fmt.Errorf("invalid request: %s: %d", key, value)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func getProviderName(id string) string {
|
||||
return strings.SplitN(id, "@", 2)[1]
|
||||
}
|
||||
|
||||
func extractType(element interface{}) string {
|
||||
v := reflect.ValueOf(element).Elem()
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
field := v.Field(i)
|
||||
if field.Kind() == reflect.Ptr && field.Elem().Kind() == reflect.Struct {
|
||||
if !field.IsNil() {
|
||||
return v.Type().Field(i).Name
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package api
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
@ -30,28 +31,31 @@ func (h Handler) getEntryPoints(rw http.ResponseWriter, request *http.Request) {
|
|||
return results[i].Name < results[j].Name
|
||||
})
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
|
||||
pageInfo, err := pagination(request, len(results))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
writeError(rw, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
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)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
writeError(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (h Handler) getEntryPoint(rw http.ResponseWriter, request *http.Request) {
|
||||
entryPointID := mux.Vars(request)["entryPointID"]
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
|
||||
ep, ok := h.staticConfig.EntryPoints[entryPointID]
|
||||
if !ok {
|
||||
http.NotFound(rw, request)
|
||||
writeError(rw, fmt.Sprintf("entry point not found: %s", entryPointID), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -60,11 +64,9 @@ func (h Handler) getEntryPoint(rw http.ResponseWriter, request *http.Request) {
|
|||
Name: entryPointID,
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
|
||||
err := json.NewEncoder(rw).Encode(result)
|
||||
if err != nil {
|
||||
log.FromContext(request.Context()).Error(err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
writeError(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,11 @@ package api
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containous/traefik/v2/pkg/config/runtime"
|
||||
"github.com/containous/traefik/v2/pkg/log"
|
||||
|
@ -17,182 +19,224 @@ type routerRepresentation struct {
|
|||
Provider string `json:"provider,omitempty"`
|
||||
}
|
||||
|
||||
func newRouterRepresentation(name string, rt *runtime.RouterInfo) routerRepresentation {
|
||||
return routerRepresentation{
|
||||
RouterInfo: rt,
|
||||
Name: name,
|
||||
Provider: getProviderName(name),
|
||||
}
|
||||
}
|
||||
|
||||
type serviceRepresentation struct {
|
||||
*runtime.ServiceInfo
|
||||
ServerStatus map[string]string `json:"serverStatus,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Provider string `json:"provider,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
func newServiceRepresentation(name string, si *runtime.ServiceInfo) serviceRepresentation {
|
||||
return serviceRepresentation{
|
||||
ServiceInfo: si,
|
||||
Name: name,
|
||||
Provider: getProviderName(name),
|
||||
ServerStatus: si.GetAllStatus(),
|
||||
Type: strings.ToLower(extractType(si.Service)),
|
||||
}
|
||||
}
|
||||
|
||||
type middlewareRepresentation struct {
|
||||
*runtime.MiddlewareInfo
|
||||
Name string `json:"name,omitempty"`
|
||||
Provider string `json:"provider,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
func newMiddlewareRepresentation(name string, mi *runtime.MiddlewareInfo) middlewareRepresentation {
|
||||
return middlewareRepresentation{
|
||||
MiddlewareInfo: mi,
|
||||
Name: name,
|
||||
Provider: getProviderName(name),
|
||||
Type: strings.ToLower(extractType(mi.Middleware)),
|
||||
}
|
||||
}
|
||||
|
||||
func (h Handler) getRouters(rw http.ResponseWriter, request *http.Request) {
|
||||
results := make([]routerRepresentation, 0, len(h.runtimeConfiguration.Routers))
|
||||
|
||||
criterion := newSearchCriterion(request.URL.Query())
|
||||
|
||||
for name, rt := range h.runtimeConfiguration.Routers {
|
||||
results = append(results, routerRepresentation{
|
||||
RouterInfo: rt,
|
||||
Name: name,
|
||||
Provider: getProviderName(name),
|
||||
})
|
||||
if keepRouter(name, rt, criterion) {
|
||||
results = append(results, newRouterRepresentation(name, rt))
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
writeError(rw, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
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)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
writeError(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (h Handler) getRouter(rw http.ResponseWriter, request *http.Request) {
|
||||
routerID := mux.Vars(request)["routerID"]
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
|
||||
router, ok := h.runtimeConfiguration.Routers[routerID]
|
||||
if !ok {
|
||||
http.NotFound(rw, request)
|
||||
writeError(rw, fmt.Sprintf("router not found: %s", routerID), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
result := routerRepresentation{
|
||||
RouterInfo: router,
|
||||
Name: routerID,
|
||||
Provider: getProviderName(routerID),
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
result := newRouterRepresentation(routerID, router)
|
||||
|
||||
err := json.NewEncoder(rw).Encode(result)
|
||||
if err != nil {
|
||||
log.FromContext(request.Context()).Error(err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
writeError(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (h Handler) getServices(rw http.ResponseWriter, request *http.Request) {
|
||||
results := make([]serviceRepresentation, 0, len(h.runtimeConfiguration.Services))
|
||||
|
||||
criterion := newSearchCriterion(request.URL.Query())
|
||||
|
||||
for name, si := range h.runtimeConfiguration.Services {
|
||||
results = append(results, serviceRepresentation{
|
||||
ServiceInfo: si,
|
||||
Name: name,
|
||||
Provider: getProviderName(name),
|
||||
ServerStatus: si.GetAllStatus(),
|
||||
})
|
||||
if keepService(name, si, criterion) {
|
||||
results = append(results, newServiceRepresentation(name, si))
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
writeError(rw, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
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)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
writeError(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (h Handler) getService(rw http.ResponseWriter, request *http.Request) {
|
||||
serviceID := mux.Vars(request)["serviceID"]
|
||||
|
||||
rw.Header().Add("Content-Type", "application/json")
|
||||
|
||||
service, ok := h.runtimeConfiguration.Services[serviceID]
|
||||
if !ok {
|
||||
http.NotFound(rw, request)
|
||||
writeError(rw, fmt.Sprintf("service not found: %s", serviceID), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
result := serviceRepresentation{
|
||||
ServiceInfo: service,
|
||||
Name: serviceID,
|
||||
Provider: getProviderName(serviceID),
|
||||
ServerStatus: service.GetAllStatus(),
|
||||
}
|
||||
|
||||
rw.Header().Add("Content-Type", "application/json")
|
||||
result := newServiceRepresentation(serviceID, service)
|
||||
|
||||
err := json.NewEncoder(rw).Encode(result)
|
||||
if err != nil {
|
||||
log.FromContext(request.Context()).Error(err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
writeError(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (h Handler) getMiddlewares(rw http.ResponseWriter, request *http.Request) {
|
||||
results := make([]middlewareRepresentation, 0, len(h.runtimeConfiguration.Middlewares))
|
||||
|
||||
criterion := newSearchCriterion(request.URL.Query())
|
||||
|
||||
for name, mi := range h.runtimeConfiguration.Middlewares {
|
||||
results = append(results, middlewareRepresentation{
|
||||
MiddlewareInfo: mi,
|
||||
Name: name,
|
||||
Provider: getProviderName(name),
|
||||
})
|
||||
if keepMiddleware(name, mi, criterion) {
|
||||
results = append(results, newMiddlewareRepresentation(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 {
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
writeError(rw, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
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)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
writeError(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (h Handler) getMiddleware(rw http.ResponseWriter, request *http.Request) {
|
||||
middlewareID := mux.Vars(request)["middlewareID"]
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
|
||||
middleware, ok := h.runtimeConfiguration.Middlewares[middlewareID]
|
||||
if !ok {
|
||||
http.NotFound(rw, request)
|
||||
writeError(rw, fmt.Sprintf("middleware not found: %s", middlewareID), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
result := middlewareRepresentation{
|
||||
MiddlewareInfo: middleware,
|
||||
Name: middlewareID,
|
||||
Provider: getProviderName(middlewareID),
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
result := newMiddlewareRepresentation(middlewareID, middleware)
|
||||
|
||||
err := json.NewEncoder(rw).Encode(result)
|
||||
if err != nil {
|
||||
log.FromContext(request.Context()).Error(err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
writeError(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func keepRouter(name string, item *runtime.RouterInfo, criterion *searchCriterion) bool {
|
||||
if criterion == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return criterion.withStatus(item.Status) && criterion.searchIn(item.Rule, name)
|
||||
}
|
||||
|
||||
func keepService(name string, item *runtime.ServiceInfo, criterion *searchCriterion) bool {
|
||||
if criterion == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return criterion.withStatus(item.Status) && criterion.searchIn(name)
|
||||
}
|
||||
|
||||
func keepMiddleware(name string, item *runtime.MiddlewareInfo, criterion *searchCriterion) bool {
|
||||
if criterion == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return criterion.withStatus(item.Status) && criterion.searchIn(name)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
@ -137,6 +138,68 @@ func TestHandler_HTTP(t *testing.T) {
|
|||
statusCode: http.StatusBadRequest,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "routers filtered by status",
|
||||
path: "/api/http/routers?status=enabled",
|
||||
conf: runtime.Configuration{
|
||||
Routers: map[string]*runtime.RouterInfo{
|
||||
"test@myprovider": {
|
||||
Router: &dynamic.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar.other`)",
|
||||
Middlewares: []string{"addPrefixTest", "auth"},
|
||||
},
|
||||
Status: runtime.StatusEnabled,
|
||||
},
|
||||
"bar@myprovider": {
|
||||
Router: &dynamic.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
Middlewares: []string{"auth", "addPrefixTest@anotherprovider"},
|
||||
},
|
||||
Status: runtime.StatusDisabled,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: expected{
|
||||
statusCode: http.StatusOK,
|
||||
nextPage: "1",
|
||||
jsonFile: "testdata/routers-filtered-status.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "routers filtered by search",
|
||||
path: "/api/http/routers?search=fii",
|
||||
conf: runtime.Configuration{
|
||||
Routers: map[string]*runtime.RouterInfo{
|
||||
"test@myprovider": {
|
||||
Router: &dynamic.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "fii-service@myprovider",
|
||||
Rule: "Host(`fii.bar.other`)",
|
||||
Middlewares: []string{"addPrefixTest", "auth"},
|
||||
},
|
||||
Status: runtime.StatusEnabled,
|
||||
},
|
||||
"bar@myprovider": {
|
||||
Router: &dynamic.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
Middlewares: []string{"auth", "addPrefixTest@anotherprovider"},
|
||||
},
|
||||
Status: runtime.StatusDisabled,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: expected{
|
||||
statusCode: http.StatusOK,
|
||||
nextPage: "1",
|
||||
jsonFile: "testdata/routers-filtered-search.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "one router by id",
|
||||
path: "/api/http/routers/bar@myprovider",
|
||||
|
@ -232,6 +295,45 @@ func TestHandler_HTTP(t *testing.T) {
|
|||
si.UpdateServerStatus("http://127.0.0.2", "UP")
|
||||
return si
|
||||
}(),
|
||||
"canary@myprovider": {
|
||||
Service: &dynamic.Service{
|
||||
Weighted: &dynamic.WeightedRoundRobin{
|
||||
Services: nil,
|
||||
Sticky: &dynamic.Sticky{
|
||||
Cookie: &dynamic.Cookie{
|
||||
Name: "chocolat",
|
||||
Secure: true,
|
||||
HTTPOnly: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: runtime.StatusEnabled,
|
||||
UsedBy: []string{"foo@myprovider"},
|
||||
},
|
||||
"mirror@myprovider": {
|
||||
Service: &dynamic.Service{
|
||||
Mirroring: &dynamic.Mirroring{
|
||||
Service: "one@myprovider",
|
||||
Mirrors: []dynamic.MirrorService{
|
||||
{
|
||||
Name: "two@myprovider",
|
||||
Percent: 10,
|
||||
},
|
||||
{
|
||||
Name: "three@myprovider",
|
||||
Percent: 15,
|
||||
},
|
||||
{
|
||||
Name: "four@myprovider",
|
||||
Percent: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: runtime.StatusEnabled,
|
||||
UsedBy: []string{"foo@myprovider"},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: expected{
|
||||
|
@ -301,6 +403,100 @@ func TestHandler_HTTP(t *testing.T) {
|
|||
jsonFile: "testdata/services-page2.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "services filtered by status",
|
||||
path: "/api/http/services?status=enabled",
|
||||
conf: runtime.Configuration{
|
||||
Services: map[string]*runtime.ServiceInfo{
|
||||
"bar@myprovider": func() *runtime.ServiceInfo {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"foo@myprovider", "test@myprovider"},
|
||||
Status: runtime.StatusEnabled,
|
||||
}
|
||||
si.UpdateServerStatus("http://127.0.0.1", "UP")
|
||||
return si
|
||||
}(),
|
||||
"baz@myprovider": func() *runtime.ServiceInfo {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"foo@myprovider"},
|
||||
Status: runtime.StatusDisabled,
|
||||
}
|
||||
si.UpdateServerStatus("http://127.0.0.2", "UP")
|
||||
return si
|
||||
}(),
|
||||
},
|
||||
},
|
||||
expected: expected{
|
||||
statusCode: http.StatusOK,
|
||||
nextPage: "1",
|
||||
jsonFile: "testdata/services-filtered-status.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "services filtered by search",
|
||||
path: "/api/http/services?search=baz",
|
||||
conf: runtime.Configuration{
|
||||
Services: map[string]*runtime.ServiceInfo{
|
||||
"bar@myprovider": func() *runtime.ServiceInfo {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"foo@myprovider", "test@myprovider"},
|
||||
Status: runtime.StatusEnabled,
|
||||
}
|
||||
si.UpdateServerStatus("http://127.0.0.1", "UP")
|
||||
return si
|
||||
}(),
|
||||
"baz@myprovider": func() *runtime.ServiceInfo {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"foo@myprovider"},
|
||||
Status: runtime.StatusDisabled,
|
||||
}
|
||||
si.UpdateServerStatus("http://127.0.0.2", "UP")
|
||||
return si
|
||||
}(),
|
||||
},
|
||||
},
|
||||
expected: expected{
|
||||
statusCode: http.StatusOK,
|
||||
nextPage: "1",
|
||||
jsonFile: "testdata/services-filtered-search.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "one service by id",
|
||||
path: "/api/http/services/bar@myprovider",
|
||||
|
@ -411,6 +607,86 @@ func TestHandler_HTTP(t *testing.T) {
|
|||
jsonFile: "testdata/middlewares.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "middlewares filtered by status",
|
||||
path: "/api/http/middlewares?status=enabled",
|
||||
conf: runtime.Configuration{
|
||||
Middlewares: map[string]*runtime.MiddlewareInfo{
|
||||
"auth@myprovider": {
|
||||
Middleware: &dynamic.Middleware{
|
||||
BasicAuth: &dynamic.BasicAuth{
|
||||
Users: []string{"admin:admin"},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"bar@myprovider", "test@myprovider"},
|
||||
Status: runtime.StatusEnabled,
|
||||
},
|
||||
"addPrefixTest@myprovider": {
|
||||
Middleware: &dynamic.Middleware{
|
||||
AddPrefix: &dynamic.AddPrefix{
|
||||
Prefix: "/titi",
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"test@myprovider"},
|
||||
Status: runtime.StatusDisabled,
|
||||
},
|
||||
"addPrefixTest@anotherprovider": {
|
||||
Middleware: &dynamic.Middleware{
|
||||
AddPrefix: &dynamic.AddPrefix{
|
||||
Prefix: "/toto",
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"bar@myprovider"},
|
||||
Status: runtime.StatusEnabled,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: expected{
|
||||
statusCode: http.StatusOK,
|
||||
nextPage: "1",
|
||||
jsonFile: "testdata/middlewares-filtered-status.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "middlewares filtered by search",
|
||||
path: "/api/http/middlewares?search=addprefixtest",
|
||||
conf: runtime.Configuration{
|
||||
Middlewares: map[string]*runtime.MiddlewareInfo{
|
||||
"auth@myprovider": {
|
||||
Middleware: &dynamic.Middleware{
|
||||
BasicAuth: &dynamic.BasicAuth{
|
||||
Users: []string{"admin:admin"},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"bar@myprovider", "test@myprovider"},
|
||||
Status: runtime.StatusEnabled,
|
||||
},
|
||||
"addPrefixTest@myprovider": {
|
||||
Middleware: &dynamic.Middleware{
|
||||
AddPrefix: &dynamic.AddPrefix{
|
||||
Prefix: "/titi",
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"test@myprovider"},
|
||||
Status: runtime.StatusDisabled,
|
||||
},
|
||||
"addPrefixTest@anotherprovider": {
|
||||
Middleware: &dynamic.Middleware{
|
||||
AddPrefix: &dynamic.AddPrefix{
|
||||
Prefix: "/toto",
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"bar@myprovider"},
|
||||
Status: runtime.StatusEnabled,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: expected{
|
||||
statusCode: http.StatusOK,
|
||||
nextPage: "1",
|
||||
jsonFile: "testdata/middlewares-filtered-search.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "all middlewares, 1 res per page, want page 2",
|
||||
path: "/api/http/middlewares?page=2&per_page=1",
|
||||
|
@ -521,6 +797,8 @@ func TestHandler_HTTP(t *testing.T) {
|
|||
rtConf := &test.conf
|
||||
// To lazily initialize the Statuses.
|
||||
rtConf.PopulateUsedBy()
|
||||
rtConf.GetRoutersByEntryPoints(context.Background(), []string{"web"}, false)
|
||||
|
||||
handler := New(static.Configuration{API: &static.API{}, Global: &static.Global{}}, rtConf)
|
||||
router := mux.NewRouter()
|
||||
handler.Append(router)
|
||||
|
|
|
@ -56,7 +56,7 @@ func (h Handler) getOverview(rw http.ResponseWriter, request *http.Request) {
|
|||
err := json.NewEncoder(rw).Encode(result)
|
||||
if err != nil {
|
||||
log.FromContext(request.Context()).Error(err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
writeError(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,11 @@ package api
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containous/traefik/v2/pkg/config/runtime"
|
||||
"github.com/containous/traefik/v2/pkg/log"
|
||||
|
@ -17,118 +19,146 @@ type tcpRouterRepresentation struct {
|
|||
Provider string `json:"provider,omitempty"`
|
||||
}
|
||||
|
||||
func newTCPRouterRepresentation(name string, rt *runtime.TCPRouterInfo) tcpRouterRepresentation {
|
||||
return tcpRouterRepresentation{
|
||||
TCPRouterInfo: rt,
|
||||
Name: name,
|
||||
Provider: getProviderName(name),
|
||||
}
|
||||
}
|
||||
|
||||
type tcpServiceRepresentation struct {
|
||||
*runtime.TCPServiceInfo
|
||||
Name string `json:"name,omitempty"`
|
||||
Provider string `json:"provider,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
func newTCPServiceRepresentation(name string, si *runtime.TCPServiceInfo) tcpServiceRepresentation {
|
||||
return tcpServiceRepresentation{
|
||||
TCPServiceInfo: si,
|
||||
Name: name,
|
||||
Provider: getProviderName(name),
|
||||
Type: strings.ToLower(extractType(si.TCPService)),
|
||||
}
|
||||
}
|
||||
|
||||
func (h Handler) getTCPRouters(rw http.ResponseWriter, request *http.Request) {
|
||||
results := make([]tcpRouterRepresentation, 0, len(h.runtimeConfiguration.TCPRouters))
|
||||
|
||||
criterion := newSearchCriterion(request.URL.Query())
|
||||
|
||||
for name, rt := range h.runtimeConfiguration.TCPRouters {
|
||||
results = append(results, tcpRouterRepresentation{
|
||||
TCPRouterInfo: rt,
|
||||
Name: name,
|
||||
Provider: getProviderName(name),
|
||||
})
|
||||
if keepTCPRouter(name, rt, criterion) {
|
||||
results = append(results, newTCPRouterRepresentation(name, rt))
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
writeError(rw, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
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)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
writeError(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (h Handler) getTCPRouter(rw http.ResponseWriter, request *http.Request) {
|
||||
routerID := mux.Vars(request)["routerID"]
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
|
||||
router, ok := h.runtimeConfiguration.TCPRouters[routerID]
|
||||
if !ok {
|
||||
http.NotFound(rw, request)
|
||||
writeError(rw, fmt.Sprintf("router not found: %s", routerID), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
result := tcpRouterRepresentation{
|
||||
TCPRouterInfo: router,
|
||||
Name: routerID,
|
||||
Provider: getProviderName(routerID),
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
result := newTCPRouterRepresentation(routerID, router)
|
||||
|
||||
err := json.NewEncoder(rw).Encode(result)
|
||||
if err != nil {
|
||||
log.FromContext(request.Context()).Error(err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
writeError(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (h Handler) getTCPServices(rw http.ResponseWriter, request *http.Request) {
|
||||
results := make([]tcpServiceRepresentation, 0, len(h.runtimeConfiguration.TCPServices))
|
||||
|
||||
criterion := newSearchCriterion(request.URL.Query())
|
||||
|
||||
for name, si := range h.runtimeConfiguration.TCPServices {
|
||||
results = append(results, tcpServiceRepresentation{
|
||||
TCPServiceInfo: si,
|
||||
Name: name,
|
||||
Provider: getProviderName(name),
|
||||
})
|
||||
if keepTCPService(name, si, criterion) {
|
||||
results = append(results, newTCPServiceRepresentation(name, si))
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
writeError(rw, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
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)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
writeError(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (h Handler) getTCPService(rw http.ResponseWriter, request *http.Request) {
|
||||
serviceID := mux.Vars(request)["serviceID"]
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
|
||||
service, ok := h.runtimeConfiguration.TCPServices[serviceID]
|
||||
if !ok {
|
||||
http.NotFound(rw, request)
|
||||
writeError(rw, fmt.Sprintf("service not found: %s", serviceID), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
result := tcpServiceRepresentation{
|
||||
TCPServiceInfo: service,
|
||||
Name: serviceID,
|
||||
Provider: getProviderName(serviceID),
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
result := newTCPServiceRepresentation(serviceID, service)
|
||||
|
||||
err := json.NewEncoder(rw).Encode(result)
|
||||
if err != nil {
|
||||
log.FromContext(request.Context()).Error(err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
writeError(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func keepTCPRouter(name string, item *runtime.TCPRouterInfo, criterion *searchCriterion) bool {
|
||||
if criterion == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return criterion.withStatus(item.Status) && criterion.searchIn(item.Rule, name)
|
||||
}
|
||||
|
||||
func keepTCPService(name string, item *runtime.TCPServiceInfo, criterion *searchCriterion) bool {
|
||||
if criterion == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return criterion.withStatus(item.Status) && criterion.searchIn(name)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
@ -112,6 +113,86 @@ func TestHandler_TCP(t *testing.T) {
|
|||
jsonFile: "testdata/tcprouters-page2.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "TCP routers filtered by status",
|
||||
path: "/api/tcp/routers?status=enabled",
|
||||
conf: runtime.Configuration{
|
||||
TCPRouters: map[string]*runtime.TCPRouterInfo{
|
||||
"test@myprovider": {
|
||||
TCPRouter: &dynamic.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar.other`)",
|
||||
TLS: &dynamic.RouterTCPTLSConfig{
|
||||
Passthrough: false,
|
||||
},
|
||||
},
|
||||
Status: runtime.StatusEnabled,
|
||||
},
|
||||
"bar@myprovider": {
|
||||
TCPRouter: &dynamic.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
Status: runtime.StatusWarning,
|
||||
},
|
||||
"foo@myprovider": {
|
||||
TCPRouter: &dynamic.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
Status: runtime.StatusDisabled,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: expected{
|
||||
statusCode: http.StatusOK,
|
||||
nextPage: "1",
|
||||
jsonFile: "testdata/tcprouters-filtered-status.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "TCP routers filtered by search",
|
||||
path: "/api/tcp/routers?search=bar@my",
|
||||
conf: runtime.Configuration{
|
||||
TCPRouters: map[string]*runtime.TCPRouterInfo{
|
||||
"test@myprovider": {
|
||||
TCPRouter: &dynamic.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar.other`)",
|
||||
TLS: &dynamic.RouterTCPTLSConfig{
|
||||
Passthrough: false,
|
||||
},
|
||||
},
|
||||
Status: runtime.StatusEnabled,
|
||||
},
|
||||
"bar@myprovider": {
|
||||
TCPRouter: &dynamic.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
Status: runtime.StatusWarning,
|
||||
},
|
||||
"foo@myprovider": {
|
||||
TCPRouter: &dynamic.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
Status: runtime.StatusDisabled,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: expected{
|
||||
statusCode: http.StatusOK,
|
||||
nextPage: "1",
|
||||
jsonFile: "testdata/tcprouters-filtered-search.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "one TCP router by id",
|
||||
path: "/api/tcp/routers/bar@myprovider",
|
||||
|
@ -219,6 +300,110 @@ func TestHandler_TCP(t *testing.T) {
|
|||
jsonFile: "testdata/tcpservices.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "tcp services filtered by status",
|
||||
path: "/api/tcp/services?status=enabled",
|
||||
conf: runtime.Configuration{
|
||||
TCPServices: map[string]*runtime.TCPServiceInfo{
|
||||
"bar@myprovider": {
|
||||
TCPService: &dynamic.TCPService{
|
||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
||||
Servers: []dynamic.TCPServer{
|
||||
{
|
||||
Address: "127.0.0.1:2345",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"foo@myprovider", "test@myprovider"},
|
||||
Status: runtime.StatusEnabled,
|
||||
},
|
||||
"baz@myprovider": {
|
||||
TCPService: &dynamic.TCPService{
|
||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
||||
Servers: []dynamic.TCPServer{
|
||||
{
|
||||
Address: "127.0.0.2:2345",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"foo@myprovider"},
|
||||
Status: runtime.StatusWarning,
|
||||
},
|
||||
"foz@myprovider": {
|
||||
TCPService: &dynamic.TCPService{
|
||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
||||
Servers: []dynamic.TCPServer{
|
||||
{
|
||||
Address: "127.0.0.2:2345",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"foo@myprovider"},
|
||||
Status: runtime.StatusDisabled,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: expected{
|
||||
statusCode: http.StatusOK,
|
||||
nextPage: "1",
|
||||
jsonFile: "testdata/tcpservices-filtered-status.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "tcp services filtered by search",
|
||||
path: "/api/tcp/services?search=baz@my",
|
||||
conf: runtime.Configuration{
|
||||
TCPServices: map[string]*runtime.TCPServiceInfo{
|
||||
"bar@myprovider": {
|
||||
TCPService: &dynamic.TCPService{
|
||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
||||
Servers: []dynamic.TCPServer{
|
||||
{
|
||||
Address: "127.0.0.1:2345",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"foo@myprovider", "test@myprovider"},
|
||||
Status: runtime.StatusEnabled,
|
||||
},
|
||||
"baz@myprovider": {
|
||||
TCPService: &dynamic.TCPService{
|
||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
||||
Servers: []dynamic.TCPServer{
|
||||
{
|
||||
Address: "127.0.0.2:2345",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"foo@myprovider"},
|
||||
Status: runtime.StatusWarning,
|
||||
},
|
||||
"foz@myprovider": {
|
||||
TCPService: &dynamic.TCPService{
|
||||
LoadBalancer: &dynamic.TCPLoadBalancerService{
|
||||
Servers: []dynamic.TCPServer{
|
||||
{
|
||||
Address: "127.0.0.2:2345",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
UsedBy: []string{"foo@myprovider"},
|
||||
Status: runtime.StatusDisabled,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: expected{
|
||||
statusCode: http.StatusOK,
|
||||
nextPage: "1",
|
||||
jsonFile: "testdata/tcpservices-filtered-search.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "all tcp services, 1 res per page, want page 2",
|
||||
path: "/api/tcp/services?page=2&per_page=1",
|
||||
|
@ -330,6 +515,10 @@ func TestHandler_TCP(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
rtConf := &test.conf
|
||||
// To lazily initialize the Statuses.
|
||||
rtConf.PopulateUsedBy()
|
||||
rtConf.GetTCPRoutersByEntryPoints(context.Background(), []string{"web"})
|
||||
|
||||
handler := New(static.Configuration{API: &static.API{}, Global: &static.Global{}}, rtConf)
|
||||
router := mux.NewRouter()
|
||||
handler.Append(router)
|
||||
|
|
1
pkg/api/testdata/middleware-auth.json
vendored
1
pkg/api/testdata/middleware-auth.json
vendored
|
@ -7,6 +7,7 @@
|
|||
"name": "auth@myprovider",
|
||||
"provider": "myprovider",
|
||||
"status": "enabled",
|
||||
"type": "basicauth",
|
||||
"usedBy": [
|
||||
"bar@myprovider",
|
||||
"test@myprovider"
|
||||
|
|
26
pkg/api/testdata/middlewares-filtered-search.json
vendored
Normal file
26
pkg/api/testdata/middlewares-filtered-search.json
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
[
|
||||
{
|
||||
"addPrefix": {
|
||||
"prefix": "/toto"
|
||||
},
|
||||
"name": "addPrefixTest@anotherprovider",
|
||||
"provider": "anotherprovider",
|
||||
"status": "enabled",
|
||||
"type": "addprefix",
|
||||
"usedBy": [
|
||||
"bar@myprovider"
|
||||
]
|
||||
},
|
||||
{
|
||||
"addPrefix": {
|
||||
"prefix": "/titi"
|
||||
},
|
||||
"name": "addPrefixTest@myprovider",
|
||||
"provider": "myprovider",
|
||||
"status": "disabled",
|
||||
"type": "addprefix",
|
||||
"usedBy": [
|
||||
"test@myprovider"
|
||||
]
|
||||
}
|
||||
]
|
29
pkg/api/testdata/middlewares-filtered-status.json
vendored
Normal file
29
pkg/api/testdata/middlewares-filtered-status.json
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
[
|
||||
{
|
||||
"addPrefix": {
|
||||
"prefix": "/toto"
|
||||
},
|
||||
"name": "addPrefixTest@anotherprovider",
|
||||
"provider": "anotherprovider",
|
||||
"status": "enabled",
|
||||
"type": "addprefix",
|
||||
"usedBy": [
|
||||
"bar@myprovider"
|
||||
]
|
||||
},
|
||||
{
|
||||
"basicAuth": {
|
||||
"users": [
|
||||
"admin:admin"
|
||||
]
|
||||
},
|
||||
"name": "auth@myprovider",
|
||||
"provider": "myprovider",
|
||||
"status": "enabled",
|
||||
"type": "basicauth",
|
||||
"usedBy": [
|
||||
"bar@myprovider",
|
||||
"test@myprovider"
|
||||
]
|
||||
}
|
||||
]
|
1
pkg/api/testdata/middlewares-page2.json
vendored
1
pkg/api/testdata/middlewares-page2.json
vendored
|
@ -6,6 +6,7 @@
|
|||
"name": "addPrefixTest@myprovider",
|
||||
"provider": "myprovider",
|
||||
"status": "enabled",
|
||||
"type": "addprefix",
|
||||
"usedBy": [
|
||||
"test@myprovider"
|
||||
]
|
||||
|
|
3
pkg/api/testdata/middlewares.json
vendored
3
pkg/api/testdata/middlewares.json
vendored
|
@ -6,6 +6,7 @@
|
|||
"name": "addPrefixTest@anotherprovider",
|
||||
"provider": "anotherprovider",
|
||||
"status": "enabled",
|
||||
"type": "addprefix",
|
||||
"usedBy": [
|
||||
"bar@myprovider"
|
||||
]
|
||||
|
@ -17,6 +18,7 @@
|
|||
"name": "addPrefixTest@myprovider",
|
||||
"provider": "myprovider",
|
||||
"status": "enabled",
|
||||
"type": "addprefix",
|
||||
"usedBy": [
|
||||
"test@myprovider"
|
||||
]
|
||||
|
@ -30,6 +32,7 @@
|
|||
"name": "auth@myprovider",
|
||||
"provider": "myprovider",
|
||||
"status": "enabled",
|
||||
"type": "basicauth",
|
||||
"usedBy": [
|
||||
"bar@myprovider",
|
||||
"test@myprovider"
|
||||
|
|
5
pkg/api/testdata/router-bar.json
vendored
5
pkg/api/testdata/router-bar.json
vendored
|
@ -10,5 +10,8 @@
|
|||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar`)",
|
||||
"service": "foo-service@myprovider",
|
||||
"status": "enabled"
|
||||
"status": "enabled",
|
||||
"using": [
|
||||
"web"
|
||||
]
|
||||
}
|
19
pkg/api/testdata/routers-filtered-search.json
vendored
Normal file
19
pkg/api/testdata/routers-filtered-search.json
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
[
|
||||
{
|
||||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"middlewares": [
|
||||
"addPrefixTest",
|
||||
"auth"
|
||||
],
|
||||
"name": "test@myprovider",
|
||||
"provider": "myprovider",
|
||||
"rule": "Host(`fii.bar.other`)",
|
||||
"service": "fii-service@myprovider",
|
||||
"status": "enabled",
|
||||
"using": [
|
||||
"web"
|
||||
]
|
||||
}
|
||||
]
|
19
pkg/api/testdata/routers-filtered-status.json
vendored
Normal file
19
pkg/api/testdata/routers-filtered-status.json
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
[
|
||||
{
|
||||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"middlewares": [
|
||||
"addPrefixTest",
|
||||
"auth"
|
||||
],
|
||||
"name": "test@myprovider",
|
||||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar.other`)",
|
||||
"service": "foo-service@myprovider",
|
||||
"status": "enabled",
|
||||
"using": [
|
||||
"web"
|
||||
]
|
||||
}
|
||||
]
|
25
pkg/api/testdata/routers-many-lastpage.json
vendored
25
pkg/api/testdata/routers-many-lastpage.json
vendored
|
@ -7,7 +7,10 @@
|
|||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar14`)",
|
||||
"service": "foo-service@myprovider",
|
||||
"status": "enabled"
|
||||
"status": "enabled",
|
||||
"using": [
|
||||
"web"
|
||||
]
|
||||
},
|
||||
{
|
||||
"entryPoints": [
|
||||
|
@ -17,7 +20,10 @@
|
|||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar15`)",
|
||||
"service": "foo-service@myprovider",
|
||||
"status": "enabled"
|
||||
"status": "enabled",
|
||||
"using": [
|
||||
"web"
|
||||
]
|
||||
},
|
||||
{
|
||||
"entryPoints": [
|
||||
|
@ -27,7 +33,10 @@
|
|||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar16`)",
|
||||
"service": "foo-service@myprovider",
|
||||
"status": "enabled"
|
||||
"status": "enabled",
|
||||
"using": [
|
||||
"web"
|
||||
]
|
||||
},
|
||||
{
|
||||
"entryPoints": [
|
||||
|
@ -37,7 +46,10 @@
|
|||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar17`)",
|
||||
"service": "foo-service@myprovider",
|
||||
"status": "enabled"
|
||||
"status": "enabled",
|
||||
"using": [
|
||||
"web"
|
||||
]
|
||||
},
|
||||
{
|
||||
"entryPoints": [
|
||||
|
@ -47,6 +59,9 @@
|
|||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar18`)",
|
||||
"service": "foo-service@myprovider",
|
||||
"status": "enabled"
|
||||
"status": "enabled",
|
||||
"using": [
|
||||
"web"
|
||||
]
|
||||
}
|
||||
]
|
5
pkg/api/testdata/routers-page2.json
vendored
5
pkg/api/testdata/routers-page2.json
vendored
|
@ -7,6 +7,9 @@
|
|||
"provider": "myprovider",
|
||||
"rule": "Host(`toto.bar`)",
|
||||
"service": "foo-service@myprovider",
|
||||
"status": "enabled"
|
||||
"status": "enabled",
|
||||
"using": [
|
||||
"web"
|
||||
]
|
||||
}
|
||||
]
|
10
pkg/api/testdata/routers.json
vendored
10
pkg/api/testdata/routers.json
vendored
|
@ -11,7 +11,10 @@
|
|||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar`)",
|
||||
"service": "foo-service@myprovider",
|
||||
"status": "enabled"
|
||||
"status": "enabled",
|
||||
"using": [
|
||||
"web"
|
||||
]
|
||||
},
|
||||
{
|
||||
"entryPoints": [
|
||||
|
@ -25,6 +28,9 @@
|
|||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar.other`)",
|
||||
"service": "foo-service@myprovider",
|
||||
"status": "enabled"
|
||||
"status": "enabled",
|
||||
"using": [
|
||||
"web"
|
||||
]
|
||||
}
|
||||
]
|
1
pkg/api/testdata/service-bar.json
vendored
1
pkg/api/testdata/service-bar.json
vendored
|
@ -13,6 +13,7 @@
|
|||
"http://127.0.0.1": "UP"
|
||||
},
|
||||
"status": "enabled",
|
||||
"type": "loadbalancer",
|
||||
"usedBy": [
|
||||
"foo@myprovider",
|
||||
"test@myprovider"
|
||||
|
|
22
pkg/api/testdata/services-filtered-search.json
vendored
Normal file
22
pkg/api/testdata/services-filtered-search.json
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
[
|
||||
{
|
||||
"loadBalancer": {
|
||||
"passHostHeader": false,
|
||||
"servers": [
|
||||
{
|
||||
"url": "http://127.0.0.2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "baz@myprovider",
|
||||
"provider": "myprovider",
|
||||
"serverStatus": {
|
||||
"http://127.0.0.2": "UP"
|
||||
},
|
||||
"status": "disabled",
|
||||
"type": "loadbalancer",
|
||||
"usedBy": [
|
||||
"foo@myprovider"
|
||||
]
|
||||
}
|
||||
]
|
23
pkg/api/testdata/services-filtered-status.json
vendored
Normal file
23
pkg/api/testdata/services-filtered-status.json
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
[
|
||||
{
|
||||
"loadBalancer": {
|
||||
"passHostHeader": false,
|
||||
"servers": [
|
||||
{
|
||||
"url": "http://127.0.0.1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "bar@myprovider",
|
||||
"provider": "myprovider",
|
||||
"serverStatus": {
|
||||
"http://127.0.0.1": "UP"
|
||||
},
|
||||
"status": "enabled",
|
||||
"type": "loadbalancer",
|
||||
"usedBy": [
|
||||
"foo@myprovider",
|
||||
"test@myprovider"
|
||||
]
|
||||
}
|
||||
]
|
1
pkg/api/testdata/services-page2.json
vendored
1
pkg/api/testdata/services-page2.json
vendored
|
@ -14,6 +14,7 @@
|
|||
"http://127.0.0.2": "UP"
|
||||
},
|
||||
"status": "enabled",
|
||||
"type": "loadbalancer",
|
||||
"usedBy": [
|
||||
"foo@myprovider"
|
||||
]
|
||||
|
|
46
pkg/api/testdata/services.json
vendored
46
pkg/api/testdata/services.json
vendored
|
@ -14,6 +14,7 @@
|
|||
"http://127.0.0.1": "UP"
|
||||
},
|
||||
"status": "enabled",
|
||||
"type": "loadbalancer",
|
||||
"usedBy": [
|
||||
"foo@myprovider",
|
||||
"test@myprovider"
|
||||
|
@ -34,6 +35,51 @@
|
|||
"http://127.0.0.2": "UP"
|
||||
},
|
||||
"status": "enabled",
|
||||
"type": "loadbalancer",
|
||||
"usedBy": [
|
||||
"foo@myprovider"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "canary@myprovider",
|
||||
"provider": "myprovider",
|
||||
"status": "enabled",
|
||||
"type": "weighted",
|
||||
"usedBy": [
|
||||
"foo@myprovider"
|
||||
],
|
||||
"weighted": {
|
||||
"sticky": {
|
||||
"cookie": {
|
||||
"httpOnly": true,
|
||||
"name": "chocolat",
|
||||
"secure": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"mirroring": {
|
||||
"mirrors": [
|
||||
{
|
||||
"name": "two@myprovider",
|
||||
"percent": 10
|
||||
},
|
||||
{
|
||||
"name": "three@myprovider",
|
||||
"percent": 15
|
||||
},
|
||||
{
|
||||
"name": "four@myprovider",
|
||||
"percent": 80
|
||||
}
|
||||
],
|
||||
"service": "one@myprovider"
|
||||
},
|
||||
"name": "mirror@myprovider",
|
||||
"provider": "myprovider",
|
||||
"status": "enabled",
|
||||
"type": "mirroring",
|
||||
"usedBy": [
|
||||
"foo@myprovider"
|
||||
]
|
||||
|
|
6
pkg/api/testdata/tcprouter-bar.json
vendored
6
pkg/api/testdata/tcprouter-bar.json
vendored
|
@ -5,5 +5,9 @@
|
|||
"name": "bar@myprovider",
|
||||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar`)",
|
||||
"service": "foo-service@myprovider"
|
||||
"service": "foo-service@myprovider",
|
||||
"status": "enabled",
|
||||
"using": [
|
||||
"web"
|
||||
]
|
||||
}
|
15
pkg/api/testdata/tcprouters-filtered-search.json
vendored
Normal file
15
pkg/api/testdata/tcprouters-filtered-search.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
[
|
||||
{
|
||||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"name": "bar@myprovider",
|
||||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar`)",
|
||||
"service": "foo-service@myprovider",
|
||||
"status": "warning",
|
||||
"using": [
|
||||
"web"
|
||||
]
|
||||
}
|
||||
]
|
18
pkg/api/testdata/tcprouters-filtered-status.json
vendored
Normal file
18
pkg/api/testdata/tcprouters-filtered-status.json
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
[
|
||||
{
|
||||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"name": "test@myprovider",
|
||||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar.other`)",
|
||||
"service": "foo-service@myprovider",
|
||||
"status": "enabled",
|
||||
"tls": {
|
||||
"passthrough": false
|
||||
},
|
||||
"using": [
|
||||
"web"
|
||||
]
|
||||
}
|
||||
]
|
6
pkg/api/testdata/tcprouters-page2.json
vendored
6
pkg/api/testdata/tcprouters-page2.json
vendored
|
@ -6,6 +6,10 @@
|
|||
"name": "baz@myprovider",
|
||||
"provider": "myprovider",
|
||||
"rule": "Host(`toto.bar`)",
|
||||
"service": "foo-service@myprovider"
|
||||
"service": "foo-service@myprovider",
|
||||
"status": "enabled",
|
||||
"using": [
|
||||
"web"
|
||||
]
|
||||
}
|
||||
]
|
15
pkg/api/testdata/tcprouters.json
vendored
15
pkg/api/testdata/tcprouters.json
vendored
|
@ -7,7 +7,10 @@
|
|||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar`)",
|
||||
"service": "foo-service@myprovider",
|
||||
"status": "warning"
|
||||
"status": "warning",
|
||||
"using": [
|
||||
"web"
|
||||
]
|
||||
},
|
||||
{
|
||||
"entryPoints": [
|
||||
|
@ -17,7 +20,10 @@
|
|||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.bar`)",
|
||||
"service": "foo-service@myprovider",
|
||||
"status": "disabled"
|
||||
"status": "disabled",
|
||||
"using": [
|
||||
"web"
|
||||
]
|
||||
},
|
||||
{
|
||||
"entryPoints": [
|
||||
|
@ -30,6 +36,9 @@
|
|||
"status": "enabled",
|
||||
"tls": {
|
||||
"passthrough": false
|
||||
}
|
||||
},
|
||||
"using": [
|
||||
"web"
|
||||
]
|
||||
}
|
||||
]
|
2
pkg/api/testdata/tcpservice-bar.json
vendored
2
pkg/api/testdata/tcpservice-bar.json
vendored
|
@ -8,6 +8,8 @@
|
|||
},
|
||||
"name": "bar@myprovider",
|
||||
"provider": "myprovider",
|
||||
"status": "enabled",
|
||||
"type": "loadbalancer",
|
||||
"usedBy": [
|
||||
"foo@myprovider",
|
||||
"test@myprovider"
|
||||
|
|
18
pkg/api/testdata/tcpservices-filtered-search.json
vendored
Normal file
18
pkg/api/testdata/tcpservices-filtered-search.json
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
[
|
||||
{
|
||||
"loadBalancer": {
|
||||
"servers": [
|
||||
{
|
||||
"address": "127.0.0.2:2345"
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "baz@myprovider",
|
||||
"provider": "myprovider",
|
||||
"status": "warning",
|
||||
"type": "loadbalancer",
|
||||
"usedBy": [
|
||||
"foo@myprovider"
|
||||
]
|
||||
}
|
||||
]
|
19
pkg/api/testdata/tcpservices-filtered-status.json
vendored
Normal file
19
pkg/api/testdata/tcpservices-filtered-status.json
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
[
|
||||
{
|
||||
"loadBalancer": {
|
||||
"servers": [
|
||||
{
|
||||
"address": "127.0.0.1:2345"
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "bar@myprovider",
|
||||
"provider": "myprovider",
|
||||
"status": "enabled",
|
||||
"type": "loadbalancer",
|
||||
"usedBy": [
|
||||
"foo@myprovider",
|
||||
"test@myprovider"
|
||||
]
|
||||
}
|
||||
]
|
2
pkg/api/testdata/tcpservices-page2.json
vendored
2
pkg/api/testdata/tcpservices-page2.json
vendored
|
@ -9,6 +9,8 @@
|
|||
},
|
||||
"name": "baz@myprovider",
|
||||
"provider": "myprovider",
|
||||
"status": "enabled",
|
||||
"type": "loadbalancer",
|
||||
"usedBy": [
|
||||
"foo@myprovider"
|
||||
]
|
||||
|
|
3
pkg/api/testdata/tcpservices.json
vendored
3
pkg/api/testdata/tcpservices.json
vendored
|
@ -10,6 +10,7 @@
|
|||
"name": "bar@myprovider",
|
||||
"provider": "myprovider",
|
||||
"status": "enabled",
|
||||
"type": "loadbalancer",
|
||||
"usedBy": [
|
||||
"foo@myprovider",
|
||||
"test@myprovider"
|
||||
|
@ -26,6 +27,7 @@
|
|||
"name": "baz@myprovider",
|
||||
"provider": "myprovider",
|
||||
"status": "warning",
|
||||
"type": "loadbalancer",
|
||||
"usedBy": [
|
||||
"foo@myprovider"
|
||||
]
|
||||
|
@ -41,6 +43,7 @@
|
|||
"name": "foz@myprovider",
|
||||
"provider": "myprovider",
|
||||
"status": "disabled",
|
||||
"type": "loadbalancer",
|
||||
"usedBy": [
|
||||
"foo@myprovider"
|
||||
]
|
||||
|
|
|
@ -3,13 +3,14 @@ package runtime
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/containous/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/containous/traefik/v2/pkg/log"
|
||||
)
|
||||
|
||||
// GetRoutersByEntryPoints returns all the http routers by entry points name and routers name
|
||||
// GetRoutersByEntryPoints returns all the http routers by entry points name and routers name.
|
||||
func (c *Configuration) GetRoutersByEntryPoints(ctx context.Context, entryPoints []string, tls bool) map[string]map[string]*RouterInfo {
|
||||
entryPointsRouters := make(map[string]map[string]*RouterInfo)
|
||||
|
||||
|
@ -19,11 +20,13 @@ func (c *Configuration) GetRoutersByEntryPoints(ctx context.Context, entryPoints
|
|||
}
|
||||
|
||||
logger := log.FromContext(log.With(ctx, log.Str(log.RouterName, rtName)))
|
||||
|
||||
eps := rt.EntryPoints
|
||||
if len(eps) == 0 {
|
||||
logger.Debugf("No entrypoint defined for this router, using the default one(s) instead: %+v", entryPoints)
|
||||
logger.Debugf("No entryPoint defined for this router, using the default one(s) instead: %+v", entryPoints)
|
||||
eps = entryPoints
|
||||
}
|
||||
|
||||
entryPointsCount := 0
|
||||
for _, entryPointName := range eps {
|
||||
if !contains(entryPoints, entryPointName) {
|
||||
|
@ -33,23 +36,44 @@ func (c *Configuration) GetRoutersByEntryPoints(ctx context.Context, entryPoints
|
|||
continue
|
||||
}
|
||||
|
||||
entryPointsCount++
|
||||
if _, ok := entryPointsRouters[entryPointName]; !ok {
|
||||
entryPointsRouters[entryPointName] = make(map[string]*RouterInfo)
|
||||
}
|
||||
|
||||
entryPointsCount++
|
||||
rt.Using = append(rt.Using, entryPointName)
|
||||
|
||||
entryPointsRouters[entryPointName][rtName] = rt
|
||||
}
|
||||
|
||||
if entryPointsCount == 0 {
|
||||
rt.AddError(fmt.Errorf("no valid entryPoint for this router"), true)
|
||||
logger.Error("no valid entryPoint for this router")
|
||||
}
|
||||
|
||||
rt.Using = unique(rt.Using)
|
||||
}
|
||||
|
||||
return entryPointsRouters
|
||||
}
|
||||
|
||||
// RouterInfo holds information about a currently running HTTP router
|
||||
func unique(src []string) []string {
|
||||
var uniq []string
|
||||
|
||||
set := make(map[string]struct{})
|
||||
for _, v := range src {
|
||||
if _, exist := set[v]; !exist {
|
||||
set[v] = struct{}{}
|
||||
uniq = append(uniq, v)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(uniq)
|
||||
|
||||
return uniq
|
||||
}
|
||||
|
||||
// RouterInfo holds information about a currently running HTTP router.
|
||||
type RouterInfo struct {
|
||||
*dynamic.Router // dynamic configuration
|
||||
// Err contains all the errors that occurred during router's creation.
|
||||
|
@ -57,7 +81,8 @@ type RouterInfo struct {
|
|||
// Status reports whether the router is disabled, in a warning state, or all good (enabled).
|
||||
// If not in "enabled" state, the reason for it should be in the list of Err.
|
||||
// It is the caller's responsibility to set the initial status.
|
||||
Status string `json:"status,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Using []string `json:"using,omitempty"` // Effective entry points used by that router.
|
||||
}
|
||||
|
||||
// AddError adds err to r.Err, if it does not already exist.
|
||||
|
@ -81,13 +106,13 @@ func (r *RouterInfo) AddError(err error, critical bool) {
|
|||
}
|
||||
}
|
||||
|
||||
// MiddlewareInfo holds information about a currently running middleware
|
||||
// MiddlewareInfo holds information about a currently running middleware.
|
||||
type MiddlewareInfo struct {
|
||||
*dynamic.Middleware // dynamic configuration
|
||||
// Err contains all the errors that occurred during service creation.
|
||||
Err []string `json:"error,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
UsedBy []string `json:"usedBy,omitempty"` // list of routers and services using that middleware
|
||||
UsedBy []string `json:"usedBy,omitempty"` // list of routers and services using that middleware.
|
||||
}
|
||||
|
||||
// AddError adds err to s.Err, if it does not already exist.
|
||||
|
@ -111,7 +136,7 @@ func (m *MiddlewareInfo) AddError(err error, critical bool) {
|
|||
}
|
||||
}
|
||||
|
||||
// ServiceInfo holds information about a currently running service
|
||||
// ServiceInfo holds information about a currently running service.
|
||||
type ServiceInfo struct {
|
||||
*dynamic.Service // dynamic configuration
|
||||
// Err contains all the errors that occurred during service creation.
|
||||
|
|
|
@ -104,6 +104,7 @@ func TestGetRoutersByEntryPoints(t *testing.T) {
|
|||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
Status: "enabled",
|
||||
Using: []string{"web"},
|
||||
},
|
||||
"foobar": {
|
||||
Router: &dynamic.Router{
|
||||
|
@ -113,6 +114,7 @@ func TestGetRoutersByEntryPoints(t *testing.T) {
|
|||
},
|
||||
Status: "warning",
|
||||
Err: []string{`entryPoint "webs" doesn't exist`},
|
||||
Using: []string{"web"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -169,6 +171,7 @@ func TestGetRoutersByEntryPoints(t *testing.T) {
|
|||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
Status: "enabled",
|
||||
Using: []string{"web"},
|
||||
},
|
||||
"foobar": {
|
||||
Router: &dynamic.Router{
|
||||
|
@ -177,6 +180,7 @@ func TestGetRoutersByEntryPoints(t *testing.T) {
|
|||
Rule: "Host(`bar.foobar`)",
|
||||
},
|
||||
Status: "enabled",
|
||||
Using: []string{"web", "webs"},
|
||||
},
|
||||
},
|
||||
"webs": {
|
||||
|
@ -188,6 +192,7 @@ func TestGetRoutersByEntryPoints(t *testing.T) {
|
|||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
Status: "enabled",
|
||||
Using: []string{"webs"},
|
||||
},
|
||||
"foobar": {
|
||||
Router: &dynamic.Router{
|
||||
|
@ -196,6 +201,7 @@ func TestGetRoutersByEntryPoints(t *testing.T) {
|
|||
Rule: "Host(`bar.foobar`)",
|
||||
},
|
||||
Status: "enabled",
|
||||
Using: []string{"web", "webs"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -2,24 +2,30 @@ package runtime
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/containous/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/containous/traefik/v2/pkg/log"
|
||||
)
|
||||
|
||||
// GetTCPRoutersByEntryPoints returns all the tcp routers by entry points name and routers name
|
||||
// GetTCPRoutersByEntryPoints returns all the tcp routers by entry points name and routers name.
|
||||
func (c *Configuration) GetTCPRoutersByEntryPoints(ctx context.Context, entryPoints []string) map[string]map[string]*TCPRouterInfo {
|
||||
entryPointsRouters := make(map[string]map[string]*TCPRouterInfo)
|
||||
|
||||
for rtName, rt := range c.TCPRouters {
|
||||
logger := log.FromContext(log.With(ctx, log.Str(log.RouterName, rtName)))
|
||||
|
||||
eps := rt.EntryPoints
|
||||
if len(eps) == 0 {
|
||||
logger.Debugf("No entryPoint defined for this router, using the default one(s) instead: %+v", entryPoints)
|
||||
eps = entryPoints
|
||||
}
|
||||
|
||||
entryPointsCount := 0
|
||||
for _, entryPointName := range eps {
|
||||
if !contains(entryPoints, entryPointName) {
|
||||
log.FromContext(log.With(ctx, log.Str(log.EntryPointName, entryPointName))).
|
||||
rt.AddError(fmt.Errorf("entryPoint %q doesn't exist", entryPointName), false)
|
||||
logger.WithField(log.EntryPointName, entryPointName).
|
||||
Errorf("entryPoint %q doesn't exist", entryPointName)
|
||||
continue
|
||||
}
|
||||
|
@ -28,21 +34,30 @@ func (c *Configuration) GetTCPRoutersByEntryPoints(ctx context.Context, entryPoi
|
|||
entryPointsRouters[entryPointName] = make(map[string]*TCPRouterInfo)
|
||||
}
|
||||
|
||||
entryPointsCount++
|
||||
rt.Using = append(rt.Using, entryPointName)
|
||||
|
||||
entryPointsRouters[entryPointName][rtName] = rt
|
||||
}
|
||||
|
||||
if entryPointsCount == 0 {
|
||||
rt.AddError(fmt.Errorf("no valid entryPoint for this router"), true)
|
||||
logger.Error("no valid entryPoint for this router")
|
||||
}
|
||||
}
|
||||
|
||||
return entryPointsRouters
|
||||
}
|
||||
|
||||
// TCPRouterInfo holds information about a currently running TCP router
|
||||
// TCPRouterInfo holds information about a currently running TCP router.
|
||||
type TCPRouterInfo struct {
|
||||
*dynamic.TCPRouter // dynamic configuration
|
||||
Err []string `json:"error,omitempty"` // initialization error
|
||||
// Status reports whether the router is disabled, in a warning state, or all good (enabled).
|
||||
// If not in "enabled" state, the reason for it should be in the list of Err.
|
||||
// It is the caller's responsibility to set the initial status.
|
||||
Status string `json:"status,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Using []string `json:"using,omitempty"` // Effective entry points used by that router.
|
||||
}
|
||||
|
||||
// AddError adds err to r.Err, if it does not already exist.
|
||||
|
@ -66,7 +81,7 @@ func (r *TCPRouterInfo) AddError(err error, critical bool) {
|
|||
}
|
||||
}
|
||||
|
||||
// TCPServiceInfo holds information about a currently running TCP service
|
||||
// TCPServiceInfo holds information about a currently running TCP service.
|
||||
type TCPServiceInfo struct {
|
||||
*dynamic.TCPService // dynamic configuration
|
||||
Err []string `json:"error,omitempty"` // initialization error
|
||||
|
|
|
@ -104,6 +104,7 @@ func TestGetTCPRoutersByEntryPoints(t *testing.T) {
|
|||
Rule: "HostSNI(`bar.foo`)",
|
||||
},
|
||||
Status: "enabled",
|
||||
Using: []string{"web"},
|
||||
},
|
||||
"foobar": {
|
||||
TCPRouter: &dynamic.TCPRouter{
|
||||
|
@ -111,7 +112,9 @@ func TestGetTCPRoutersByEntryPoints(t *testing.T) {
|
|||
Service: "foobar-service@myprovider",
|
||||
Rule: "HostSNI(`bar.foobar`)",
|
||||
},
|
||||
Status: "enabled",
|
||||
Status: "warning",
|
||||
Err: []string{`entryPoint "webs" doesn't exist`},
|
||||
Using: []string{"web"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -168,6 +171,7 @@ func TestGetTCPRoutersByEntryPoints(t *testing.T) {
|
|||
Rule: "HostSNI(`bar.foo`)",
|
||||
},
|
||||
Status: "enabled",
|
||||
Using: []string{"web"},
|
||||
},
|
||||
"foobar": {
|
||||
TCPRouter: &dynamic.TCPRouter{
|
||||
|
@ -176,6 +180,7 @@ func TestGetTCPRoutersByEntryPoints(t *testing.T) {
|
|||
Rule: "HostSNI(`bar.foobar`)",
|
||||
},
|
||||
Status: "enabled",
|
||||
Using: []string{"web", "webs"},
|
||||
},
|
||||
},
|
||||
"webs": {
|
||||
|
@ -187,6 +192,7 @@ func TestGetTCPRoutersByEntryPoints(t *testing.T) {
|
|||
Rule: "HostSNI(`foo.bar`)",
|
||||
},
|
||||
Status: "enabled",
|
||||
Using: []string{"webs"},
|
||||
},
|
||||
"foobar": {
|
||||
TCPRouter: &dynamic.TCPRouter{
|
||||
|
@ -195,6 +201,7 @@ func TestGetTCPRoutersByEntryPoints(t *testing.T) {
|
|||
Rule: "HostSNI(`bar.foobar`)",
|
||||
},
|
||||
Status: "enabled",
|
||||
Using: []string{"web", "webs"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -144,7 +144,6 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
|
|||
}
|
||||
conf.Routers[serviceName].TLS = tlsConf
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue