2019-07-15 15:04:04 +00:00
|
|
|
package runtime
|
2019-05-16 08:58:06 +00:00
|
|
|
|
|
|
|
import (
|
2019-06-17 16:14:08 +00:00
|
|
|
"context"
|
2019-05-16 08:58:06 +00:00
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
|
2019-07-15 15:04:04 +00:00
|
|
|
"github.com/containous/traefik/pkg/config/dynamic"
|
2019-05-16 08:58:06 +00:00
|
|
|
"github.com/containous/traefik/pkg/log"
|
|
|
|
)
|
|
|
|
|
2019-07-15 15:04:04 +00:00
|
|
|
// Status of the router/service
|
|
|
|
const (
|
|
|
|
StatusEnabled = "enabled"
|
|
|
|
StatusDisabled = "disabled"
|
|
|
|
StatusWarning = "warning"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Configuration holds the information about the currently running traefik instance.
|
|
|
|
type Configuration struct {
|
2019-05-16 08:58:06 +00:00
|
|
|
Routers map[string]*RouterInfo `json:"routers,omitempty"`
|
|
|
|
Middlewares map[string]*MiddlewareInfo `json:"middlewares,omitempty"`
|
|
|
|
Services map[string]*ServiceInfo `json:"services,omitempty"`
|
|
|
|
TCPRouters map[string]*TCPRouterInfo `json:"tcpRouters,omitempty"`
|
|
|
|
TCPServices map[string]*TCPServiceInfo `json:"tcpServices,omitempty"`
|
|
|
|
}
|
|
|
|
|
2019-07-15 15:04:04 +00:00
|
|
|
// NewConfig returns a Configuration initialized with the given conf. It never returns nil.
|
|
|
|
func NewConfig(conf dynamic.Configuration) *Configuration {
|
2019-05-16 08:58:06 +00:00
|
|
|
if conf.HTTP == nil && conf.TCP == nil {
|
2019-07-15 15:04:04 +00:00
|
|
|
return &Configuration{}
|
2019-05-16 08:58:06 +00:00
|
|
|
}
|
|
|
|
|
2019-07-15 15:04:04 +00:00
|
|
|
runtimeConfig := &Configuration{}
|
2019-05-16 08:58:06 +00:00
|
|
|
|
|
|
|
if conf.HTTP != nil {
|
|
|
|
routers := conf.HTTP.Routers
|
|
|
|
if len(routers) > 0 {
|
|
|
|
runtimeConfig.Routers = make(map[string]*RouterInfo, len(routers))
|
|
|
|
for k, v := range routers {
|
2019-07-15 15:04:04 +00:00
|
|
|
runtimeConfig.Routers[k] = &RouterInfo{Router: v, Status: StatusEnabled}
|
2019-05-16 08:58:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
services := conf.HTTP.Services
|
|
|
|
if len(services) > 0 {
|
|
|
|
runtimeConfig.Services = make(map[string]*ServiceInfo, len(services))
|
|
|
|
for k, v := range services {
|
2019-07-15 15:04:04 +00:00
|
|
|
runtimeConfig.Services[k] = &ServiceInfo{Service: v, Status: StatusEnabled}
|
2019-05-16 08:58:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
middlewares := conf.HTTP.Middlewares
|
|
|
|
if len(middlewares) > 0 {
|
|
|
|
runtimeConfig.Middlewares = make(map[string]*MiddlewareInfo, len(middlewares))
|
|
|
|
for k, v := range middlewares {
|
|
|
|
runtimeConfig.Middlewares[k] = &MiddlewareInfo{Middleware: v}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if conf.TCP != nil {
|
|
|
|
if len(conf.TCP.Routers) > 0 {
|
|
|
|
runtimeConfig.TCPRouters = make(map[string]*TCPRouterInfo, len(conf.TCP.Routers))
|
|
|
|
for k, v := range conf.TCP.Routers {
|
2019-07-15 15:04:04 +00:00
|
|
|
runtimeConfig.TCPRouters[k] = &TCPRouterInfo{TCPRouter: v, Status: StatusEnabled}
|
2019-05-16 08:58:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(conf.TCP.Services) > 0 {
|
|
|
|
runtimeConfig.TCPServices = make(map[string]*TCPServiceInfo, len(conf.TCP.Services))
|
|
|
|
for k, v := range conf.TCP.Services {
|
2019-07-15 15:04:04 +00:00
|
|
|
runtimeConfig.TCPServices[k] = &TCPServiceInfo{TCPService: v, Status: StatusEnabled}
|
2019-05-16 08:58:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return runtimeConfig
|
|
|
|
}
|
|
|
|
|
|
|
|
// PopulateUsedBy populates all the UsedBy lists of the underlying fields of r,
|
|
|
|
// based on the relations between the included services, routers, and middlewares.
|
2019-07-15 15:04:04 +00:00
|
|
|
func (r *Configuration) PopulateUsedBy() {
|
2019-05-16 08:58:06 +00:00
|
|
|
if r == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
logger := log.WithoutContext()
|
|
|
|
|
|
|
|
for routerName, routerInfo := range r.Routers {
|
2019-07-15 15:04:04 +00:00
|
|
|
// lazily initialize Status in case caller forgot to do it
|
|
|
|
if routerInfo.Status == "" {
|
|
|
|
routerInfo.Status = StatusEnabled
|
|
|
|
}
|
|
|
|
|
2019-05-16 08:58:06 +00:00
|
|
|
providerName := getProviderName(routerName)
|
|
|
|
if providerName == "" {
|
|
|
|
logger.WithField(log.RouterName, routerName).Error("router name is not fully qualified")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, midName := range routerInfo.Router.Middlewares {
|
|
|
|
fullMidName := getQualifiedName(providerName, midName)
|
|
|
|
if _, ok := r.Middlewares[fullMidName]; !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
r.Middlewares[fullMidName].UsedBy = append(r.Middlewares[fullMidName].UsedBy, routerName)
|
|
|
|
}
|
|
|
|
|
|
|
|
serviceName := getQualifiedName(providerName, routerInfo.Router.Service)
|
|
|
|
if _, ok := r.Services[serviceName]; !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
r.Services[serviceName].UsedBy = append(r.Services[serviceName].UsedBy, routerName)
|
|
|
|
}
|
|
|
|
|
2019-07-15 15:04:04 +00:00
|
|
|
for k, serviceInfo := range r.Services {
|
|
|
|
// lazily initialize Status in case caller forgot to do it
|
|
|
|
if serviceInfo.Status == "" {
|
|
|
|
serviceInfo.Status = StatusEnabled
|
|
|
|
}
|
|
|
|
|
2019-05-16 08:58:06 +00:00
|
|
|
sort.Strings(r.Services[k].UsedBy)
|
|
|
|
}
|
|
|
|
|
|
|
|
for k := range r.Middlewares {
|
|
|
|
sort.Strings(r.Middlewares[k].UsedBy)
|
|
|
|
}
|
|
|
|
|
|
|
|
for routerName, routerInfo := range r.TCPRouters {
|
2019-07-15 15:04:04 +00:00
|
|
|
// lazily initialize Status in case caller forgot to do it
|
|
|
|
if routerInfo.Status == "" {
|
|
|
|
routerInfo.Status = StatusEnabled
|
|
|
|
}
|
|
|
|
|
2019-05-16 08:58:06 +00:00
|
|
|
providerName := getProviderName(routerName)
|
|
|
|
if providerName == "" {
|
|
|
|
logger.WithField(log.RouterName, routerName).Error("tcp router name is not fully qualified")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
serviceName := getQualifiedName(providerName, routerInfo.TCPRouter.Service)
|
|
|
|
if _, ok := r.TCPServices[serviceName]; !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
r.TCPServices[serviceName].UsedBy = append(r.TCPServices[serviceName].UsedBy, routerName)
|
|
|
|
}
|
|
|
|
|
2019-07-15 15:04:04 +00:00
|
|
|
for k, serviceInfo := range r.TCPServices {
|
|
|
|
// lazily initialize Status in case caller forgot to do it
|
|
|
|
if serviceInfo.Status == "" {
|
|
|
|
serviceInfo.Status = StatusEnabled
|
|
|
|
}
|
|
|
|
|
2019-05-16 08:58:06 +00:00
|
|
|
sort.Strings(r.TCPServices[k].UsedBy)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-17 16:14:08 +00:00
|
|
|
func contains(entryPoints []string, entryPointName string) bool {
|
|
|
|
for _, name := range entryPoints {
|
|
|
|
if name == entryPointName {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2019-07-15 15:04:04 +00:00
|
|
|
// GetRoutersByEntryPoints returns all the http routers by entry points name and routers name
|
|
|
|
func (r *Configuration) GetRoutersByEntryPoints(ctx context.Context, entryPoints []string, tls bool) map[string]map[string]*RouterInfo {
|
2019-06-17 16:14:08 +00:00
|
|
|
entryPointsRouters := make(map[string]map[string]*RouterInfo)
|
|
|
|
|
|
|
|
for rtName, rt := range r.Routers {
|
|
|
|
if (tls && rt.TLS == nil) || (!tls && rt.TLS != nil) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
eps := rt.EntryPoints
|
|
|
|
if len(eps) == 0 {
|
|
|
|
eps = entryPoints
|
|
|
|
}
|
|
|
|
for _, entryPointName := range eps {
|
|
|
|
if !contains(entryPoints, entryPointName) {
|
|
|
|
log.FromContext(log.With(ctx, log.Str(log.EntryPointName, entryPointName))).
|
|
|
|
Errorf("entryPoint %q doesn't exist", entryPointName)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := entryPointsRouters[entryPointName]; !ok {
|
|
|
|
entryPointsRouters[entryPointName] = make(map[string]*RouterInfo)
|
|
|
|
}
|
|
|
|
|
|
|
|
entryPointsRouters[entryPointName][rtName] = rt
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return entryPointsRouters
|
|
|
|
}
|
|
|
|
|
2019-07-15 15:04:04 +00:00
|
|
|
// GetTCPRoutersByEntryPoints returns all the tcp routers by entry points name and routers name
|
|
|
|
func (r *Configuration) GetTCPRoutersByEntryPoints(ctx context.Context, entryPoints []string) map[string]map[string]*TCPRouterInfo {
|
2019-06-17 16:14:08 +00:00
|
|
|
entryPointsRouters := make(map[string]map[string]*TCPRouterInfo)
|
|
|
|
|
|
|
|
for rtName, rt := range r.TCPRouters {
|
|
|
|
eps := rt.EntryPoints
|
|
|
|
if len(eps) == 0 {
|
|
|
|
eps = entryPoints
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, entryPointName := range eps {
|
|
|
|
if !contains(entryPoints, entryPointName) {
|
|
|
|
log.FromContext(log.With(ctx, log.Str(log.EntryPointName, entryPointName))).
|
|
|
|
Errorf("entryPoint %q doesn't exist", entryPointName)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := entryPointsRouters[entryPointName]; !ok {
|
|
|
|
entryPointsRouters[entryPointName] = make(map[string]*TCPRouterInfo)
|
|
|
|
}
|
|
|
|
|
|
|
|
entryPointsRouters[entryPointName][rtName] = rt
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return entryPointsRouters
|
|
|
|
}
|
|
|
|
|
2019-05-16 08:58:06 +00:00
|
|
|
// RouterInfo holds information about a currently running HTTP router
|
|
|
|
type RouterInfo struct {
|
2019-07-15 15:04:04 +00:00
|
|
|
*dynamic.Router // dynamic configuration
|
|
|
|
// Err contains all the errors that occurred during router's creation.
|
|
|
|
Err []string `json:"error,omitempty"`
|
|
|
|
// Status reports whether the router is disabled, in a warning state, or all good (enabled).
|
|
|
|
// 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"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddError adds err to r.Err, if it does not already exist.
|
|
|
|
// If critical is set, r is marked as disabled.
|
|
|
|
func (r *RouterInfo) AddError(err error, critical bool) {
|
|
|
|
for _, value := range r.Err {
|
|
|
|
if value == err.Error() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
r.Err = append(r.Err, err.Error())
|
|
|
|
if critical {
|
|
|
|
r.Status = StatusDisabled
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// only set it to "warning" if not already in a worse state
|
|
|
|
if r.Status != StatusDisabled {
|
|
|
|
r.Status = StatusWarning
|
|
|
|
}
|
2019-05-16 08:58:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TCPRouterInfo holds information about a currently running TCP router
|
|
|
|
type TCPRouterInfo struct {
|
2019-07-15 15:04:04 +00:00
|
|
|
*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"`
|
2019-05-16 08:58:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// MiddlewareInfo holds information about a currently running middleware
|
|
|
|
type MiddlewareInfo struct {
|
2019-07-15 15:04:04 +00:00
|
|
|
*dynamic.Middleware // dynamic configuration
|
|
|
|
// Err contains all the errors that occurred during service creation.
|
|
|
|
Err []string `json:"error,omitempty"`
|
|
|
|
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.
|
|
|
|
// If critical is set, m is marked as disabled.
|
|
|
|
func (m *MiddlewareInfo) AddError(err error) {
|
|
|
|
for _, value := range m.Err {
|
|
|
|
if value == err.Error() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m.Err = append(m.Err, err.Error())
|
2019-05-16 08:58:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ServiceInfo holds information about a currently running service
|
|
|
|
type ServiceInfo struct {
|
2019-07-15 15:04:04 +00:00
|
|
|
*dynamic.Service // dynamic configuration
|
|
|
|
// Err contains all the errors that occurred during service creation.
|
|
|
|
Err []string `json:"error,omitempty"`
|
|
|
|
// Status reports whether the service is disabled, in a warning state, or all good (enabled).
|
|
|
|
// 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"`
|
|
|
|
UsedBy []string `json:"usedBy,omitempty"` // list of routers using that service
|
|
|
|
|
|
|
|
serverStatusMu sync.RWMutex
|
|
|
|
serverStatus map[string]string // keyed by server URL
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddError adds err to s.Err, if it does not already exist.
|
|
|
|
// If critical is set, s is marked as disabled.
|
|
|
|
func (s *ServiceInfo) AddError(err error, critical bool) {
|
|
|
|
for _, value := range s.Err {
|
|
|
|
if value == err.Error() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2019-05-16 08:58:06 +00:00
|
|
|
|
2019-07-15 15:04:04 +00:00
|
|
|
s.Err = append(s.Err, err.Error())
|
|
|
|
if critical {
|
|
|
|
s.Status = StatusDisabled
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// only set it to "warning" if not already in a worse state
|
|
|
|
if s.Status != StatusDisabled {
|
|
|
|
s.Status = StatusWarning
|
|
|
|
}
|
2019-05-16 08:58:06 +00:00
|
|
|
}
|
|
|
|
|
2019-07-15 15:04:04 +00:00
|
|
|
// UpdateServerStatus sets the status of the server in the ServiceInfo.
|
2019-05-16 08:58:06 +00:00
|
|
|
// It is the responsibility of the caller to check that s is not nil.
|
2019-07-15 15:04:04 +00:00
|
|
|
func (s *ServiceInfo) UpdateServerStatus(server string, status string) {
|
|
|
|
s.serverStatusMu.Lock()
|
|
|
|
defer s.serverStatusMu.Unlock()
|
2019-05-16 08:58:06 +00:00
|
|
|
|
2019-07-15 15:04:04 +00:00
|
|
|
if s.serverStatus == nil {
|
|
|
|
s.serverStatus = make(map[string]string)
|
2019-05-16 08:58:06 +00:00
|
|
|
}
|
2019-07-15 15:04:04 +00:00
|
|
|
s.serverStatus[server] = status
|
2019-05-16 08:58:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetAllStatus returns all the statuses of all the servers in ServiceInfo.
|
|
|
|
// It is the responsibility of the caller to check that s is not nil
|
|
|
|
func (s *ServiceInfo) GetAllStatus() map[string]string {
|
2019-07-15 15:04:04 +00:00
|
|
|
s.serverStatusMu.RLock()
|
|
|
|
defer s.serverStatusMu.RUnlock()
|
2019-05-16 08:58:06 +00:00
|
|
|
|
2019-07-15 15:04:04 +00:00
|
|
|
if len(s.serverStatus) == 0 {
|
2019-05-16 08:58:06 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-07-15 15:04:04 +00:00
|
|
|
allStatus := make(map[string]string, len(s.serverStatus))
|
|
|
|
for k, v := range s.serverStatus {
|
2019-05-16 08:58:06 +00:00
|
|
|
allStatus[k] = v
|
|
|
|
}
|
|
|
|
return allStatus
|
|
|
|
}
|
|
|
|
|
|
|
|
// TCPServiceInfo holds information about a currently running TCP service
|
|
|
|
type TCPServiceInfo struct {
|
2019-07-15 15:04:04 +00:00
|
|
|
*dynamic.TCPService // dynamic configuration
|
|
|
|
Err error `json:"error,omitempty"` // initialization error
|
|
|
|
// Status reports whether the service is disabled, in a warning state, or all good (enabled).
|
|
|
|
// If not in "enabled" state, the reason for it should be in the list of Err.
|
|
|
|
// It is the caller's responsibility to set the initial status.
|
|
|
|
Status string `json:"status,omitempty"`
|
|
|
|
UsedBy []string `json:"usedBy,omitempty"` // list of routers using that service
|
2019-05-16 08:58:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func getProviderName(elementName string) string {
|
2019-06-19 22:40:05 +00:00
|
|
|
parts := strings.Split(elementName, "@")
|
2019-05-16 08:58:06 +00:00
|
|
|
if len(parts) > 1 {
|
2019-06-21 07:54:04 +00:00
|
|
|
return parts[1]
|
2019-05-16 08:58:06 +00:00
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func getQualifiedName(provider, elementName string) string {
|
2019-06-19 22:40:05 +00:00
|
|
|
parts := strings.Split(elementName, "@")
|
2019-05-16 08:58:06 +00:00
|
|
|
if len(parts) == 1 {
|
2019-06-21 07:54:04 +00:00
|
|
|
return elementName + "@" + provider
|
2019-05-16 08:58:06 +00:00
|
|
|
}
|
|
|
|
return elementName
|
|
|
|
}
|