Dynamic Configuration Refactoring
This commit is contained in:
parent
d3ae88f108
commit
a09dfa3ce1
452 changed files with 21023 additions and 9419 deletions
|
@ -1,6 +1,7 @@
|
|||
package metrics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
|
@ -11,7 +12,7 @@ import (
|
|||
)
|
||||
|
||||
var datadogClient = dogstatsd.New("traefik.", kitlog.LoggerFunc(func(keyvals ...interface{}) error {
|
||||
log.Info(keyvals)
|
||||
log.WithoutContext().WithField(log.MetricsProviderName, "datadog").Info(keyvals)
|
||||
return nil
|
||||
}))
|
||||
|
||||
|
@ -34,9 +35,9 @@ const (
|
|||
)
|
||||
|
||||
// RegisterDatadog registers the metrics pusher if this didn't happen yet and creates a datadog Registry instance.
|
||||
func RegisterDatadog(config *types.Datadog) Registry {
|
||||
func RegisterDatadog(ctx context.Context, config *types.Datadog) Registry {
|
||||
if datadogTicker == nil {
|
||||
datadogTicker = initDatadogClient(config)
|
||||
datadogTicker = initDatadogClient(ctx, config)
|
||||
}
|
||||
|
||||
registry := &standardRegistry{
|
||||
|
@ -58,14 +59,14 @@ func RegisterDatadog(config *types.Datadog) Registry {
|
|||
return registry
|
||||
}
|
||||
|
||||
func initDatadogClient(config *types.Datadog) *time.Ticker {
|
||||
func initDatadogClient(ctx context.Context, config *types.Datadog) *time.Ticker {
|
||||
address := config.Address
|
||||
if len(address) == 0 {
|
||||
address = "localhost:8125"
|
||||
}
|
||||
pushInterval, err := time.ParseDuration(config.PushInterval)
|
||||
if err != nil {
|
||||
log.Warnf("Unable to parse %s into pushInterval, using 10s as default value", config.PushInterval)
|
||||
log.FromContext(ctx).Warnf("Unable to parse %s from config.PushInterval: using 10s as the default value", config.PushInterval)
|
||||
pushInterval = 10 * time.Second
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package metrics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
@ -15,7 +16,7 @@ func TestDatadog(t *testing.T) {
|
|||
// This is needed to make sure that UDP Listener listens for data a bit longer, otherwise it will quit after a millisecond
|
||||
udp.Timeout = 5 * time.Second
|
||||
|
||||
datadogRegistry := RegisterDatadog(&types.Datadog{Address: ":18125", PushInterval: "1s"})
|
||||
datadogRegistry := RegisterDatadog(context.Background(), &types.Datadog{Address: ":18125", PushInterval: "1s"})
|
||||
defer StopDatadog()
|
||||
|
||||
if !datadogRegistry.IsEnabled() {
|
||||
|
|
|
@ -2,6 +2,7 @@ package metrics
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
|
@ -39,13 +40,18 @@ const (
|
|||
influxDBServerUpName = "traefik.backend.server.up"
|
||||
)
|
||||
|
||||
const (
|
||||
protocolHTTP = "http"
|
||||
protocolUDP = "udp"
|
||||
)
|
||||
|
||||
// RegisterInfluxDB registers the metrics pusher if this didn't happen yet and creates a InfluxDB Registry instance.
|
||||
func RegisterInfluxDB(config *types.InfluxDB) Registry {
|
||||
func RegisterInfluxDB(ctx context.Context, config *types.InfluxDB) Registry {
|
||||
if influxDBClient == nil {
|
||||
influxDBClient = initInfluxDBClient(config)
|
||||
influxDBClient = initInfluxDBClient(ctx, config)
|
||||
}
|
||||
if influxDBTicker == nil {
|
||||
influxDBTicker = initInfluxDBTicker(config)
|
||||
influxDBTicker = initInfluxDBTicker(ctx, config)
|
||||
}
|
||||
|
||||
return &standardRegistry{
|
||||
|
@ -66,30 +72,32 @@ func RegisterInfluxDB(config *types.InfluxDB) Registry {
|
|||
}
|
||||
|
||||
// initInfluxDBTicker creates a influxDBClient
|
||||
func initInfluxDBClient(config *types.InfluxDB) *influx.Influx {
|
||||
func initInfluxDBClient(ctx context.Context, config *types.InfluxDB) *influx.Influx {
|
||||
logger := log.FromContext(ctx)
|
||||
|
||||
// TODO deprecated: move this switch into configuration.SetEffectiveConfiguration when web provider will be removed.
|
||||
switch config.Protocol {
|
||||
case "udp":
|
||||
case protocolUDP:
|
||||
if len(config.Database) > 0 || len(config.RetentionPolicy) > 0 {
|
||||
log.Warn("Database and RetentionPolicy are only used when protocol is http.")
|
||||
logger.Warn("Database and RetentionPolicy options have no effect with UDP.")
|
||||
config.Database = ""
|
||||
config.RetentionPolicy = ""
|
||||
}
|
||||
case "http":
|
||||
case protocolHTTP:
|
||||
if u, err := url.Parse(config.Address); err == nil {
|
||||
if u.Scheme != "http" && u.Scheme != "https" {
|
||||
log.Warnf("InfluxDB address %s should specify a scheme of http or https, defaulting to http.", config.Address)
|
||||
logger.Warnf("InfluxDB address %s should specify a scheme (http or https): falling back on HTTP.", config.Address)
|
||||
config.Address = "http://" + config.Address
|
||||
}
|
||||
} else {
|
||||
log.Errorf("Unable to parse influxdb address: %v, defaulting to udp.", err)
|
||||
config.Protocol = "udp"
|
||||
logger.Errorf("Unable to parse the InfluxDB address %v: falling back on UDP.", err)
|
||||
config.Protocol = protocolUDP
|
||||
config.Database = ""
|
||||
config.RetentionPolicy = ""
|
||||
}
|
||||
default:
|
||||
log.Warnf("Unsupported protocol: %s, defaulting to udp.", config.Protocol)
|
||||
config.Protocol = "udp"
|
||||
logger.Warnf("Unsupported protocol %s: falling back on UDP.", config.Protocol)
|
||||
config.Protocol = protocolUDP
|
||||
config.Database = ""
|
||||
config.RetentionPolicy = ""
|
||||
}
|
||||
|
@ -101,16 +109,16 @@ func initInfluxDBClient(config *types.InfluxDB) *influx.Influx {
|
|||
RetentionPolicy: config.RetentionPolicy,
|
||||
},
|
||||
kitlog.LoggerFunc(func(keyvals ...interface{}) error {
|
||||
log.Info(keyvals)
|
||||
log.WithoutContext().WithField(log.MetricsProviderName, "influxdb").Info(keyvals)
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
|
||||
// initInfluxDBTicker initializes metrics pusher
|
||||
func initInfluxDBTicker(config *types.InfluxDB) *time.Ticker {
|
||||
func initInfluxDBTicker(ctx context.Context, config *types.InfluxDB) *time.Ticker {
|
||||
pushInterval, err := time.ParseDuration(config.PushInterval)
|
||||
if err != nil {
|
||||
log.Warnf("Unable to parse %s into pushInterval, using 10s as default value", config.PushInterval)
|
||||
log.FromContext(ctx).Warnf("Unable to parse %s from config.PushInterval: using 10s as the default value", config.PushInterval)
|
||||
pushInterval = 10 * time.Second
|
||||
}
|
||||
|
||||
|
@ -144,8 +152,10 @@ func (w *influxDBWriter) Write(bp influxdb.BatchPoints) error {
|
|||
defer c.Close()
|
||||
|
||||
if writeErr := c.Write(bp); writeErr != nil {
|
||||
log.Errorf("Error writing to influx: %s", writeErr.Error())
|
||||
if handleErr := w.handleWriteError(c, writeErr); handleErr != nil {
|
||||
ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "influxdb"))
|
||||
log.FromContext(ctx).Errorf("Error while writing to InfluxDB: %s", writeErr.Error())
|
||||
|
||||
if handleErr := w.handleWriteError(ctx, c, writeErr); handleErr != nil {
|
||||
return handleErr
|
||||
}
|
||||
// Retry write after successful handling of writeErr
|
||||
|
@ -168,8 +178,8 @@ func (w *influxDBWriter) initWriteClient() (influxdb.Client, error) {
|
|||
})
|
||||
}
|
||||
|
||||
func (w *influxDBWriter) handleWriteError(c influxdb.Client, writeErr error) error {
|
||||
if w.config.Protocol != "http" {
|
||||
func (w *influxDBWriter) handleWriteError(ctx context.Context, c influxdb.Client, writeErr error) error {
|
||||
if w.config.Protocol != protocolHTTP {
|
||||
return writeErr
|
||||
}
|
||||
|
||||
|
@ -184,7 +194,9 @@ func (w *influxDBWriter) handleWriteError(c influxdb.Client, writeErr error) err
|
|||
qStr = fmt.Sprintf("%s WITH NAME \"%s\"", qStr, w.config.RetentionPolicy)
|
||||
}
|
||||
|
||||
log.Debugf("Influx database does not exist, attempting to create with query: %s", qStr)
|
||||
logger := log.FromContext(ctx)
|
||||
|
||||
logger.Debugf("InfluxDB database not found: attempting to create one with %s", qStr)
|
||||
|
||||
q := influxdb.NewQuery(qStr, "", "")
|
||||
response, queryErr := c.Query(q)
|
||||
|
@ -192,10 +204,10 @@ func (w *influxDBWriter) handleWriteError(c influxdb.Client, writeErr error) err
|
|||
queryErr = response.Error()
|
||||
}
|
||||
if queryErr != nil {
|
||||
log.Errorf("Error creating InfluxDB database: %s", queryErr)
|
||||
logger.Errorf("Error while creating the InfluxDB database %s", queryErr)
|
||||
return queryErr
|
||||
}
|
||||
|
||||
log.Debugf("Successfully created influx database: %s", w.config.Database)
|
||||
logger.Debugf("Successfully created the InfluxDB database %s", w.config.Database)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package metrics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
@ -19,7 +20,7 @@ func TestInfluxDB(t *testing.T) {
|
|||
// This is needed to make sure that UDP Listener listens for data a bit longer, otherwise it will quit after a millisecond
|
||||
udp.Timeout = 5 * time.Second
|
||||
|
||||
influxDBRegistry := RegisterInfluxDB(&types.InfluxDB{Address: ":8089", PushInterval: "1s"})
|
||||
influxDBRegistry := RegisterInfluxDB(context.Background(), &types.InfluxDB{Address: ":8089", PushInterval: "1s"})
|
||||
defer StopInfluxDB()
|
||||
|
||||
if !influxDBRegistry.IsEnabled() {
|
||||
|
@ -79,7 +80,7 @@ func TestInfluxDBHTTP(t *testing.T) {
|
|||
}))
|
||||
defer ts.Close()
|
||||
|
||||
influxDBRegistry := RegisterInfluxDB(&types.InfluxDB{Address: ts.URL, Protocol: "http", PushInterval: "1s", Database: "test", RetentionPolicy: "autogen"})
|
||||
influxDBRegistry := RegisterInfluxDB(context.Background(), &types.InfluxDB{Address: ts.URL, Protocol: "http", PushInterval: "1s", Database: "test", RetentionPolicy: "autogen"})
|
||||
defer StopInfluxDB()
|
||||
|
||||
if !influxDBRegistry.IsEnabled() {
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
package metrics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/containous/mux"
|
||||
"github.com/containous/traefik/config"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/containous/traefik/types"
|
||||
|
@ -60,17 +62,17 @@ var promState = newPrometheusState()
|
|||
// PrometheusHandler exposes Prometheus routes.
|
||||
type PrometheusHandler struct{}
|
||||
|
||||
// AddRoutes adds Prometheus routes on a router.
|
||||
func (h PrometheusHandler) AddRoutes(router *mux.Router) {
|
||||
// Append adds Prometheus routes on a router.
|
||||
func (h PrometheusHandler) Append(router *mux.Router) {
|
||||
router.Methods(http.MethodGet).Path("/metrics").Handler(promhttp.Handler())
|
||||
}
|
||||
|
||||
// RegisterPrometheus registers all Prometheus metrics.
|
||||
// It must be called only once and failing to register the metrics will lead to a panic.
|
||||
func RegisterPrometheus(config *types.Prometheus) Registry {
|
||||
func RegisterPrometheus(ctx context.Context, config *types.Prometheus) Registry {
|
||||
standardRegistry := initStandardRegistry(config)
|
||||
|
||||
if !registerPromState() {
|
||||
if !registerPromState(ctx) {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -172,13 +174,14 @@ func initStandardRegistry(config *types.Prometheus) Registry {
|
|||
}
|
||||
}
|
||||
|
||||
func registerPromState() bool {
|
||||
func registerPromState(ctx context.Context) bool {
|
||||
if err := stdprometheus.Register(promState); err != nil {
|
||||
logger := log.FromContext(ctx)
|
||||
if _, ok := err.(stdprometheus.AlreadyRegisteredError); !ok {
|
||||
log.Errorf("Unable to register Traefik to Prometheus: %v", err)
|
||||
logger.Errorf("Unable to register Traefik to Prometheus: %v", err)
|
||||
return false
|
||||
}
|
||||
log.Debug("Prometheus collector already registered.")
|
||||
logger.Debug("Prometheus collector already registered.")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -186,23 +189,24 @@ func registerPromState() bool {
|
|||
// OnConfigurationUpdate receives the current configuration from Traefik.
|
||||
// It then converts the configuration to the optimized package internal format
|
||||
// and sets it to the promState.
|
||||
func OnConfigurationUpdate(configurations types.Configurations) {
|
||||
func OnConfigurationUpdate(configurations config.Configurations) {
|
||||
dynamicConfig := newDynamicConfig()
|
||||
|
||||
for _, config := range configurations {
|
||||
for _, frontend := range config.Frontends {
|
||||
for _, entrypointName := range frontend.EntryPoints {
|
||||
dynamicConfig.entrypoints[entrypointName] = true
|
||||
}
|
||||
}
|
||||
|
||||
for backendName, backend := range config.Backends {
|
||||
dynamicConfig.backends[backendName] = make(map[string]bool)
|
||||
for _, server := range backend.Servers {
|
||||
dynamicConfig.backends[backendName][server.URL] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
// FIXME metrics
|
||||
// for _, config := range configurations {
|
||||
// for _, frontend := range config.Frontends {
|
||||
// for _, entrypointName := range frontend.EntryPoints {
|
||||
// dynamicConfig.entrypoints[entrypointName] = true
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// for backendName, backend := range config.Backends {
|
||||
// dynamicConfig.backends[backendName] = make(map[string]bool)
|
||||
// for _, server := range backend.Servers {
|
||||
// dynamicConfig.backends[backendName][server.URL] = true
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
promState.SetDynamicConfig(dynamicConfig)
|
||||
}
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
package metrics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/config"
|
||||
th "github.com/containous/traefik/testhelpers"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
@ -69,8 +71,7 @@ func TestRegisterPromState(t *testing.T) {
|
|||
initStandardRegistry(prom)
|
||||
}
|
||||
|
||||
promReg := registerPromState()
|
||||
if promReg != false {
|
||||
if registerPromState(context.Background()) {
|
||||
actualNbRegistries++
|
||||
}
|
||||
|
||||
|
@ -101,7 +102,7 @@ func TestPrometheus(t *testing.T) {
|
|||
// Reset state of global promState.
|
||||
defer promState.reset()
|
||||
|
||||
prometheusRegistry := RegisterPrometheus(&types.Prometheus{})
|
||||
prometheusRegistry := RegisterPrometheus(context.Background(), &types.Prometheus{})
|
||||
defer prometheus.Unregister(promState)
|
||||
|
||||
if !prometheusRegistry.IsEnabled() {
|
||||
|
@ -266,21 +267,27 @@ func TestPrometheus(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPrometheusMetricRemoval(t *testing.T) {
|
||||
// FIXME metrics
|
||||
t.Skip("waiting for metrics")
|
||||
|
||||
// Reset state of global promState.
|
||||
defer promState.reset()
|
||||
|
||||
prometheusRegistry := RegisterPrometheus(&types.Prometheus{})
|
||||
prometheusRegistry := RegisterPrometheus(context.Background(), &types.Prometheus{})
|
||||
defer prometheus.Unregister(promState)
|
||||
|
||||
configurations := make(types.Configurations)
|
||||
configurations := make(config.Configurations)
|
||||
configurations["providerName"] = th.BuildConfiguration(
|
||||
th.WithFrontends(
|
||||
th.WithFrontend("backend1", th.WithEntryPoints("entrypoint1")),
|
||||
th.WithRouters(
|
||||
th.WithRouter("foo",
|
||||
th.WithServiceName("bar")),
|
||||
),
|
||||
th.WithBackends(
|
||||
th.WithBackendNew("backend1", th.WithServersNew(th.WithServerNew("http://localhost:9000"))),
|
||||
th.WithLoadBalancerServices(th.WithService("bar",
|
||||
th.WithLBMethod("wrr"),
|
||||
th.WithServers(th.WithServer("http://localhost:9000"))),
|
||||
),
|
||||
)
|
||||
|
||||
OnConfigurationUpdate(configurations)
|
||||
|
||||
// Register some metrics manually that are not part of the active configuration.
|
||||
|
@ -321,7 +328,7 @@ func TestPrometheusRemovedMetricsReset(t *testing.T) {
|
|||
// Reset state of global promState.
|
||||
defer promState.reset()
|
||||
|
||||
prometheusRegistry := RegisterPrometheus(&types.Prometheus{})
|
||||
prometheusRegistry := RegisterPrometheus(context.Background(), &types.Prometheus{})
|
||||
defer prometheus.Unregister(promState)
|
||||
|
||||
labelNamesValues := []string{
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package metrics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
|
@ -11,7 +12,7 @@ import (
|
|||
)
|
||||
|
||||
var statsdClient = statsd.New("traefik.", kitlog.LoggerFunc(func(keyvals ...interface{}) error {
|
||||
log.Info(keyvals)
|
||||
log.WithoutContext().WithField(log.MetricsProviderName, "statsd").Info(keyvals)
|
||||
return nil
|
||||
}))
|
||||
|
||||
|
@ -33,9 +34,9 @@ const (
|
|||
)
|
||||
|
||||
// RegisterStatsd registers the metrics pusher if this didn't happen yet and creates a statsd Registry instance.
|
||||
func RegisterStatsd(config *types.Statsd) Registry {
|
||||
func RegisterStatsd(ctx context.Context, config *types.Statsd) Registry {
|
||||
if statsdTicker == nil {
|
||||
statsdTicker = initStatsdTicker(config)
|
||||
statsdTicker = initStatsdTicker(ctx, config)
|
||||
}
|
||||
|
||||
return &standardRegistry{
|
||||
|
@ -56,14 +57,14 @@ func RegisterStatsd(config *types.Statsd) Registry {
|
|||
}
|
||||
|
||||
// initStatsdTicker initializes metrics pusher and creates a statsdClient if not created already
|
||||
func initStatsdTicker(config *types.Statsd) *time.Ticker {
|
||||
func initStatsdTicker(ctx context.Context, config *types.Statsd) *time.Ticker {
|
||||
address := config.Address
|
||||
if len(address) == 0 {
|
||||
address = "localhost:8125"
|
||||
}
|
||||
pushInterval, err := time.ParseDuration(config.PushInterval)
|
||||
if err != nil {
|
||||
log.Warnf("Unable to parse %s into pushInterval, using 10s as default value", config.PushInterval)
|
||||
log.FromContext(ctx).Warnf("Unable to parse %s from config.PushInterval: using 10s as the default value", config.PushInterval)
|
||||
pushInterval = 10 * time.Second
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package metrics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -14,7 +15,7 @@ func TestStatsD(t *testing.T) {
|
|||
// This is needed to make sure that UDP Listener listens for data a bit longer, otherwise it will quit after a millisecond
|
||||
udp.Timeout = 5 * time.Second
|
||||
|
||||
statsdRegistry := RegisterStatsd(&types.Statsd{Address: ":18125", PushInterval: "1s"})
|
||||
statsdRegistry := RegisterStatsd(context.Background(), &types.Statsd{Address: ":18125", PushInterval: "1s"})
|
||||
defer StopStatsd()
|
||||
|
||||
if !statsdRegistry.IsEnabled() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue