From ab8d7d2e7819f7472129fbc093e347c90c45ff64 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Wed, 14 Sep 2022 10:56:08 +0200 Subject: [PATCH] Remove Pilot support --- SECURITY.md | 3 +- cmd/traefik/plugins.go | 13 - cmd/traefik/traefik.go | 28 +- docs/content/deprecation/features.md | 13 +- docs/content/middlewares/http/overview.md | 2 +- docs/content/migration/v2.md | 6 + .../reference/static-configuration/cli-ref.md | 6 - .../reference/static-configuration/env-ref.md | 6 - .../reference/static-configuration/file.toml | 4 - .../reference/static-configuration/file.yaml | 3 - internal/gendoc.go | 4 + pkg/config/static/pilot.go | 9 +- pkg/config/static/static_config.go | 10 +- pkg/metrics/pilot.go | 317 --------------- pkg/metrics/pilot_test.go | 365 ------------------ pkg/pilot/pilot.go | 252 ------------ pkg/pilot/pilot_test.go | 225 ----------- pkg/plugins/client.go | 13 +- pkg/redactor/redactor_config_test.go | 4 - .../testdata/anonymized-dynamic-config.json | 2 +- .../testdata/anonymized-static-config.json | 3 - .../testdata/secured-dynamic-config.json | 2 +- pkg/version/version.go | 9 +- 23 files changed, 35 insertions(+), 1264 deletions(-) delete mode 100644 pkg/metrics/pilot.go delete mode 100644 pkg/metrics/pilot_test.go delete mode 100644 pkg/pilot/pilot.go delete mode 100644 pkg/pilot/pilot_test.go diff --git a/SECURITY.md b/SECURITY.md index f67f7b50c..6dc1bd740 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,7 +1,6 @@ # Security Policy -We strongly advise you to register your Traefik instances to [Pilot](https://pilot.traefik.io) to be notified of security advisories that apply to your Traefik version. -You can also join our security mailing list to be aware of the latest announcements from our security team. +You can join our security mailing list to be aware of the latest announcements from our security team. You can subscribe sending a mail to security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security). Reported vulnerabilities can be found on [cve.mitre.org](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=traefik). diff --git a/cmd/traefik/plugins.go b/cmd/traefik/plugins.go index dd709eee9..f94535f92 100644 --- a/cmd/traefik/plugins.go +++ b/cmd/traefik/plugins.go @@ -30,7 +30,6 @@ func initPlugins(staticCfg *static.Configuration) (*plugins.Client, map[string]p if hasPlugins(staticCfg) { opts := plugins.ClientOptions{ Output: outputDir, - Token: getPilotToken(staticCfg), } var err error @@ -75,18 +74,6 @@ func checkUniquePluginNames(e *static.Experimental) error { return nil } -func isPilotEnabled(staticCfg *static.Configuration) bool { - return staticCfg.Pilot != nil && staticCfg.Pilot.Token != "" -} - -func getPilotToken(staticCfg *static.Configuration) string { - if staticCfg.Pilot == nil { - return "" - } - - return staticCfg.Pilot.Token -} - func hasPlugins(staticCfg *static.Configuration) bool { return staticCfg.Experimental != nil && len(staticCfg.Experimental.Plugins) > 0 } diff --git a/cmd/traefik/traefik.go b/cmd/traefik/traefik.go index 0c42e4c36..f54477375 100644 --- a/cmd/traefik/traefik.go +++ b/cmd/traefik/traefik.go @@ -32,7 +32,6 @@ import ( "github.com/traefik/traefik/v2/pkg/metrics" "github.com/traefik/traefik/v2/pkg/middlewares/accesslog" "github.com/traefik/traefik/v2/pkg/middlewares/capture" - "github.com/traefik/traefik/v2/pkg/pilot" "github.com/traefik/traefik/v2/pkg/provider/acme" "github.com/traefik/traefik/v2/pkg/provider/aggregator" "github.com/traefik/traefik/v2/pkg/provider/hub" @@ -204,22 +203,8 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err return nil, err } - // Pilot - - var aviator *pilot.Pilot - var pilotRegistry *metrics.PilotRegistry - if isPilotEnabled(staticConfiguration) { - pilotRegistry = metrics.RegisterPilot() - - aviator = pilot.New(staticConfiguration.Pilot.Token, pilotRegistry, routinesPool) - - routinesPool.GoCtx(func(ctx context.Context) { - aviator.Tick(ctx) - }) - } - if staticConfiguration.Pilot != nil { - log.WithoutContext().Warn("Traefik Pilot is deprecated and will be removed soon. Please check our Blog for migration instructions later this year.") + log.WithoutContext().Warn("Traefik Pilot has been removed.") } // Plugins @@ -263,9 +248,6 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err // Metrics metricRegistries := registerMetricClients(staticConfiguration.Metrics) - if pilotRegistry != nil { - metricRegistries = append(metricRegistries, pilotRegistry) - } metricsRegistry := metrics.NewMultiRegistry(metricRegistries) // Service manager factory @@ -315,7 +297,7 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err }) // Switch router - watcher.AddListener(switchRouter(routerFactory, serverEntryPointsTCP, serverEntryPointsUDP, aviator)) + watcher.AddListener(switchRouter(routerFactory, serverEntryPointsTCP, serverEntryPointsUDP)) // Metrics if metricsRegistry.IsEpEnabled() || metricsRegistry.IsSvcEnabled() { @@ -391,16 +373,12 @@ func getDefaultsEntrypoints(staticConfiguration *static.Configuration) []string return defaultEntryPoints } -func switchRouter(routerFactory *server.RouterFactory, serverEntryPointsTCP server.TCPEntryPoints, serverEntryPointsUDP server.UDPEntryPoints, aviator *pilot.Pilot) func(conf dynamic.Configuration) { +func switchRouter(routerFactory *server.RouterFactory, serverEntryPointsTCP server.TCPEntryPoints, serverEntryPointsUDP server.UDPEntryPoints) func(conf dynamic.Configuration) { return func(conf dynamic.Configuration) { rtConf := runtime.NewConfig(conf) routers, udpRouters := routerFactory.CreateRouters(rtConf) - if aviator != nil { - aviator.SetDynamicConfiguration(conf) - } - serverEntryPointsTCP.Switch(routers) serverEntryPointsUDP.Switch(udpRouters) } diff --git a/docs/content/deprecation/features.md b/docs/content/deprecation/features.md index 3440fda5b..8837af814 100644 --- a/docs/content/deprecation/features.md +++ b/docs/content/deprecation/features.md @@ -4,22 +4,19 @@ This page is maintained and updated periodically to reflect our roadmap and any | Feature | Deprecated | End of Support | Removal | |-------------------------------------------------------------|------------|----------------|---------| -| [Pilot Dashboard (Metrics)](#pilot-dashboard-metrics) | 2.7 | 2.8 | 3.0 | -| [Pilot Plugins](#pilot-plugins) | 2.7 | 2.8 | 3.0 | +| [Pilot](#pilot) | 2.7 | 2.8 | 2.9 | | [Consul Enterprise Namespace](#consul-enterprise-namespace) | 2.8 | N/A | 3.0 | | [TLS 1.0 and 1.1 Support](#tls-10-and-11) | N/A | 2.8 | N/A | ## Impact -### Pilot Dashboard (Metrics) +### Pilot Metrics will continue to function normally up to 2.8, when they will be disabled. -In 3.0, the Pilot platform and all Traefik integration code will be permanently removed. +In 2.9, the Pilot platform and all Traefik integration code will be permanently removed. -### Pilot Plugins - -Starting on 2.7 the pilot token will not be a requirement anymore. -Since 2.8, a [new plugin catalog](https://plugins.traefik.io) is available, decoupled from pilot. +Starting on 2.7 the pilot token will not be a requirement anymore for plugins. +Since 2.8, a [new plugin catalog](https://plugins.traefik.io) is available, decoupled from Pilot. ### Consul Enterprise Namespace diff --git a/docs/content/middlewares/http/overview.md b/docs/content/middlewares/http/overview.md index 8d281f663..07096315e 100644 --- a/docs/content/middlewares/http/overview.md +++ b/docs/content/middlewares/http/overview.md @@ -156,6 +156,6 @@ http: ## Community Middlewares -Please take a look at the community-contributed plugins in the [plugin catalog](https://pilot.traefik.io/plugins). +Please take a look at the community-contributed plugins in the [plugin catalog](https://plugins.traefik.io/plugins). {!traefik-for-business-applications.md!} diff --git a/docs/content/migration/v2.md b/docs/content/migration/v2.md index 9fda2a651..1fe8c9f13 100644 --- a/docs/content/migration/v2.md +++ b/docs/content/migration/v2.md @@ -484,3 +484,9 @@ Since `v2.5.0`, the `PreferServerCipherSuites` is [deprecated and ignored](https in `v2.8.2` the `preferServerCipherSuites` option is also deprecated and ignored in Traefik. In `v2.8.2`, Traefik now reject certificates signed with the SHA-1 hash function. ([details](https://tip.golang.org/doc/go1.18#sha1)) + +## v2.9 + +### Traefik Pilot + +In `v2.9`, Traefik Pilot support has been removed. diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md index 060d882f9..05cf9e89a 100644 --- a/docs/content/reference/static-configuration/cli-ref.md +++ b/docs/content/reference/static-configuration/cli-ref.md @@ -378,12 +378,6 @@ Prefix to use for metrics collection. (Default: ```traefik```) `--metrics.statsd.pushinterval`: StatsD push interval. (Default: ```10```) -`--pilot.dashboard`: -Enable Traefik Pilot in the dashboard. (Default: ```true```) - -`--pilot.token`: -Traefik Pilot token. - `--ping`: Enable ping. (Default: ```false```) diff --git a/docs/content/reference/static-configuration/env-ref.md b/docs/content/reference/static-configuration/env-ref.md index f255ff58d..d808a5a90 100644 --- a/docs/content/reference/static-configuration/env-ref.md +++ b/docs/content/reference/static-configuration/env-ref.md @@ -378,12 +378,6 @@ Prefix to use for metrics collection. (Default: ```traefik```) `TRAEFIK_METRICS_STATSD_PUSHINTERVAL`: StatsD push interval. (Default: ```10```) -`TRAEFIK_PILOT_DASHBOARD`: -Enable Traefik Pilot in the dashboard. (Default: ```true```) - -`TRAEFIK_PILOT_TOKEN`: -Traefik Pilot token. - `TRAEFIK_PING`: Enable ping. (Default: ```false```) diff --git a/docs/content/reference/static-configuration/file.toml b/docs/content/reference/static-configuration/file.toml index a1b4bdbd3..5141798e9 100644 --- a/docs/content/reference/static-configuration/file.toml +++ b/docs/content/reference/static-configuration/file.toml @@ -437,10 +437,6 @@ entryPoint = "foobar" [certificatesResolvers.CertificateResolver1.acme.tlsChallenge] -[pilot] - token = "foobar" - dashboard = true - [hub] [hub.tls] insecure = true diff --git a/docs/content/reference/static-configuration/file.yaml b/docs/content/reference/static-configuration/file.yaml index 04aae4093..6a03c22f2 100644 --- a/docs/content/reference/static-configuration/file.yaml +++ b/docs/content/reference/static-configuration/file.yaml @@ -462,9 +462,6 @@ certificatesResolvers: httpChallenge: entryPoint: foobar tlsChallenge: {} -pilot: - token: foobar - dashboard: true hub: tls: insecure: true diff --git a/internal/gendoc.go b/internal/gendoc.go index b761debc8..afc6320f9 100644 --- a/internal/gendoc.go +++ b/internal/gendoc.go @@ -64,6 +64,10 @@ THIS FILE MUST NOT BE EDITED BY HAND continue } + if strings.HasPrefix(flat.Name, "pilot.") || strings.HasPrefix(flat.Name, "TRAEFIK_PILOT_") { + continue + } + if prefix == "" { w.writeln("`" + prefix + strings.ReplaceAll(flat.Name, "[0]", "_n") + "`: ") } else { diff --git a/pkg/config/static/pilot.go b/pkg/config/static/pilot.go index f3699a729..9831aa495 100644 --- a/pkg/config/static/pilot.go +++ b/pkg/config/static/pilot.go @@ -3,11 +3,6 @@ package static // Pilot Configuration related to Traefik Pilot. // Deprecated. type Pilot struct { - Token string `description:"Traefik Pilot token." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" loggable:"false"` - Dashboard bool `description:"Enable Traefik Pilot in the dashboard." json:"dashboard,omitempty" toml:"dashboard,omitempty" yaml:"dashboard,omitempty"` -} - -// SetDefaults sets the default values. -func (p *Pilot) SetDefaults() { - p.Dashboard = true + Token string `description:"Traefik Pilot token. (Deprecated)" json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" loggable:"false"` + Dashboard bool `description:"Enable Traefik Pilot in the dashboard. (Deprecated)" json:"dashboard,omitempty" toml:"dashboard,omitempty" yaml:"dashboard,omitempty"` } diff --git a/pkg/config/static/static_config.go b/pkg/config/static/static_config.go index 5eb8130d6..090a1e058 100644 --- a/pkg/config/static/static_config.go +++ b/pkg/config/static/static_config.go @@ -79,7 +79,7 @@ type Configuration struct { CertificatesResolvers map[string]CertificateResolver `description:"Certificates resolvers configuration." json:"certificatesResolvers,omitempty" toml:"certificatesResolvers,omitempty" yaml:"certificatesResolvers,omitempty" export:"true"` // Deprecated. - Pilot *Pilot `description:"Traefik Pilot configuration." json:"pilot,omitempty" toml:"pilot,omitempty" yaml:"pilot,omitempty" export:"true"` + Pilot *Pilot `description:"Traefik Pilot configuration (Deprecated)." json:"pilot,omitempty" toml:"pilot,omitempty" yaml:"pilot,omitempty" export:"true"` Hub *hub.Provider `description:"Traefik Hub configuration." json:"hub,omitempty" toml:"hub,omitempty" yaml:"hub,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` @@ -250,16 +250,10 @@ func (c *Configuration) SetEffectiveConfiguration() { } // Enable anonymous usage when pilot is enabled. - if c.Pilot != nil && c.Pilot.Token != "" { + if c.Pilot != nil { c.Global.SendAnonymousUsage = true } - // Create Pilot struct to apply default value on undefined configuration. - if c.Pilot == nil { - c.Pilot = &Pilot{} - c.Pilot.SetDefaults() - } - // Disable Gateway API provider if not enabled in experimental. if c.Experimental == nil || !c.Experimental.KubernetesGateway { c.Providers.KubernetesGateway = nil diff --git a/pkg/metrics/pilot.go b/pkg/metrics/pilot.go deleted file mode 100644 index 653bd455e..000000000 --- a/pkg/metrics/pilot.go +++ /dev/null @@ -1,317 +0,0 @@ -package metrics - -import ( - "strings" - "sync" - "time" - - "github.com/go-kit/kit/metrics" - "github.com/go-kit/kit/metrics/generic" -) - -const ( - // server meta information. - pilotConfigPrefix = "config" - pilotConfigReloadsTotalName = pilotConfigPrefix + "ReloadsTotal" - pilotConfigReloadsFailuresTotalName = pilotConfigPrefix + "ReloadsFailureTotal" - pilotConfigLastReloadSuccessName = pilotConfigPrefix + "LastReloadSuccess" - pilotConfigLastReloadFailureName = pilotConfigPrefix + "LastReloadFailure" - - // entry point. - pilotEntryPointPrefix = "entrypoint" - pilotEntryPointReqsTotalName = pilotEntryPointPrefix + "RequestsTotal" - pilotEntryPointReqsTLSTotalName = pilotEntryPointPrefix + "RequestsTLSTotal" - pilotEntryPointReqDurationName = pilotEntryPointPrefix + "RequestDurationSeconds" - pilotEntryPointOpenConnsName = pilotEntryPointPrefix + "OpenConnections" - - // service level. - pilotServicePrefix = "service" - pilotServiceReqsTotalName = pilotServicePrefix + "RequestsTotal" - pilotServiceReqsTLSTotalName = pilotServicePrefix + "RequestsTLSTotal" - pilotServiceReqDurationName = pilotServicePrefix + "RequestDurationSeconds" - pilotServiceOpenConnsName = pilotServicePrefix + "OpenConnections" - pilotServiceRetriesTotalName = pilotServicePrefix + "RetriesTotal" - pilotServiceServerUpName = pilotServicePrefix + "ServerUp" -) - -const root = "value" - -// RegisterPilot registers all Pilot metrics. -func RegisterPilot() *PilotRegistry { - standardRegistry := &standardRegistry{ - epEnabled: true, - svcEnabled: true, - } - - pr := &PilotRegistry{ - standardRegistry: standardRegistry, - counters: make(map[string]*pilotCounter), - gauges: make(map[string]*pilotGauge), - histograms: make(map[string]*pilotHistogram), - } - - standardRegistry.configReloadsCounter = pr.newCounter(pilotConfigReloadsTotalName) - standardRegistry.configReloadsFailureCounter = pr.newCounter(pilotConfigReloadsFailuresTotalName) - standardRegistry.lastConfigReloadSuccessGauge = pr.newGauge(pilotConfigLastReloadSuccessName) - standardRegistry.lastConfigReloadFailureGauge = pr.newGauge(pilotConfigLastReloadFailureName) - - standardRegistry.entryPointReqsCounter = pr.newCounter(pilotEntryPointReqsTotalName) - standardRegistry.entryPointReqsTLSCounter = pr.newCounter(pilotEntryPointReqsTLSTotalName) - standardRegistry.entryPointReqDurationHistogram, _ = NewHistogramWithScale(pr.newHistogram(pilotEntryPointReqDurationName), time.Millisecond) - standardRegistry.entryPointOpenConnsGauge = pr.newGauge(pilotEntryPointOpenConnsName) - - standardRegistry.serviceReqsCounter = pr.newCounter(pilotServiceReqsTotalName) - standardRegistry.serviceReqsTLSCounter = pr.newCounter(pilotServiceReqsTLSTotalName) - standardRegistry.serviceReqDurationHistogram, _ = NewHistogramWithScale(pr.newHistogram(pilotServiceReqDurationName), time.Millisecond) - standardRegistry.serviceOpenConnsGauge = pr.newGauge(pilotServiceOpenConnsName) - standardRegistry.serviceRetriesCounter = pr.newCounter(pilotServiceRetriesTotalName) - standardRegistry.serviceServerUpGauge = pr.newGauge(pilotServiceServerUpName) - - return pr -} - -// PilotMetric is a representation of a metric. -type PilotMetric struct { - Name string `json:"name"` - Type string `json:"type"` - Observations map[string]interface{} `json:"observations"` -} - -type pilotHistogramObservation struct { - Total float64 `json:"total"` - Count float64 `json:"count"` -} - -// PilotRegistry represents the pilots metrics registry. -type PilotRegistry struct { - counters map[string]*pilotCounter - gauges map[string]*pilotGauge - histograms map[string]*pilotHistogram - - *standardRegistry -} - -// newCounter register and returns a new pilotCounter. -func (pr *PilotRegistry) newCounter(name string) *pilotCounter { - c := newPilotCounter(name) - pr.counters[name] = c - - return c -} - -// newGauge register and returns a new pilotGauge. -func (pr *PilotRegistry) newGauge(name string) *pilotGauge { - g := newPilotGauge(name) - pr.gauges[name] = g - - return g -} - -// newHistogram register and returns a new pilotHistogram. -func (pr *PilotRegistry) newHistogram(name string) *pilotHistogram { - h := newPilotHistogram(name) - pr.histograms[name] = h - - return h -} - -// Data exports the metrics: metrics name -> labels -> values. -func (pr *PilotRegistry) Data() []PilotMetric { - var pilotMetrics []PilotMetric - - for name, counter := range pr.counters { - pilotMetric := PilotMetric{ - Name: name, - Type: "COUNTER", - Observations: make(map[string]interface{}), - } - pilotMetrics = append(pilotMetrics, pilotMetric) - - counter.counters.Range(func(key, value interface{}) bool { - labels := key.(string) - pc := value.(*pilotCounter) - - if labels == "" { - labels = root - } - - if labels == root || len(pc.c.LabelValues())%2 == 0 { - pilotMetric.Observations[labels] = pc.c.Value() - } - - return true - }) - } - - for name, gauge := range pr.gauges { - pilotMetric := PilotMetric{ - Name: name, - Type: "GAUGE", - Observations: make(map[string]interface{}), - } - pilotMetrics = append(pilotMetrics, pilotMetric) - - gauge.gauges.Range(func(key, value interface{}) bool { - labels := key.(string) - pg := value.(*pilotGauge) - - if labels == "" { - labels = root - } - - if labels == root || len(pg.g.LabelValues())%2 == 0 { - pilotMetric.Observations[labels] = pg.g.Value() - } - - return true - }) - } - - for name, histogram := range pr.histograms { - pilotMetric := PilotMetric{ - Name: name, - Type: "HISTOGRAM", - Observations: make(map[string]interface{}), - } - pilotMetrics = append(pilotMetrics, pilotMetric) - - histogram.histograms.Range(func(key, value interface{}) bool { - labels := key.(string) - ph := value.(*pilotHistogram) - - if labels == "" { - labels = root - } - - if labels == root || len(ph.labels)%2 == 0 { - pilotMetric.Observations[labels] = &pilotHistogramObservation{ - Total: ph.total.Value(), - Count: ph.count.Value(), - } - } - - return true - }) - } - - return pilotMetrics -} - -type pilotCounter struct { - c *generic.Counter - counters *sync.Map -} - -func newPilotCounter(name string) *pilotCounter { - return &pilotCounter{ - c: generic.NewCounter(name), - counters: &sync.Map{}, - } -} - -// With returns a new pilotCounter with the given labels. -func (c *pilotCounter) With(labels ...string) metrics.Counter { - newCounter := c.c.With(labels...).(*generic.Counter) - newCounter.ValueReset() - - return &pilotCounter{ - c: newCounter, - counters: c.counters, - } -} - -// Add adds the given delta to the counter. -func (c *pilotCounter) Add(delta float64) { - labelsKey := strings.Join(c.c.LabelValues(), ",") - - pc, _ := c.counters.LoadOrStore(labelsKey, c) - - pc.(*pilotCounter).c.Add(delta) -} - -type pilotGauge struct { - g *generic.Gauge - gauges *sync.Map -} - -func newPilotGauge(name string) *pilotGauge { - return &pilotGauge{ - g: generic.NewGauge(name), - gauges: &sync.Map{}, - } -} - -// With returns a new pilotGauge with the given labels. -func (g *pilotGauge) With(labels ...string) metrics.Gauge { - newGauge := g.g.With(labels...).(*generic.Gauge) - newGauge.Set(0) - - return &pilotGauge{ - g: newGauge, - gauges: g.gauges, - } -} - -// Set sets the given value to the gauge. -func (g *pilotGauge) Set(value float64) { - labelsKey := strings.Join(g.g.LabelValues(), ",") - - pg, _ := g.gauges.LoadOrStore(labelsKey, g) - - pg.(*pilotGauge).g.Set(value) -} - -// Add adds the given delta to the gauge. -func (g *pilotGauge) Add(delta float64) { - labelsKey := strings.Join(g.g.LabelValues(), ",") - - pg, _ := g.gauges.LoadOrStore(labelsKey, g) - - pg.(*pilotGauge).g.Add(delta) -} - -type pilotHistogram struct { - name string - labels []string - count *generic.Counter - total *generic.Counter - histograms *sync.Map -} - -func newPilotHistogram(name string) *pilotHistogram { - return &pilotHistogram{ - name: name, - labels: make([]string, 0), - count: &generic.Counter{}, - total: &generic.Counter{}, - histograms: &sync.Map{}, - } -} - -// With returns a new pilotHistogram with the given labels. -func (h *pilotHistogram) With(labels ...string) metrics.Histogram { - var newLabels []string - - newLabels = append(newLabels, h.labels...) - newLabels = append(newLabels, labels...) - - return &pilotHistogram{ - name: h.name, - labels: newLabels, - count: &generic.Counter{}, - total: &generic.Counter{}, - histograms: h.histograms, - } -} - -// Observe records a new value into the histogram. -func (h *pilotHistogram) Observe(value float64) { - labelsKey := strings.Join(h.labels, ",") - - ph, _ := h.histograms.LoadOrStore(labelsKey, h) - - pHisto := ph.(*pilotHistogram) - - pHisto.count.Add(1) - pHisto.total.Add(value) -} diff --git a/pkg/metrics/pilot_test.go b/pkg/metrics/pilot_test.go deleted file mode 100644 index 451c1eb93..000000000 --- a/pkg/metrics/pilot_test.go +++ /dev/null @@ -1,365 +0,0 @@ -package metrics - -import ( - "net/http" - "strconv" - "strings" - "testing" - "time" - - "github.com/go-kit/kit/metrics" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestPilotCounter(t *testing.T) { - rootCounter := newPilotCounter("rootCounter") - - // Checks that a counter without labels can be incremented. - rootCounter.Add(1) - assertPilotCounterValue(t, 1.0, "", rootCounter) - - // Checks that a counter with labels can be incremented. - counterWithLabels := rootCounter.With("foo", "bar", "foo", "buz") - - counterWithLabels.Add(1) - assertPilotCounterValue(t, 1.0, "foo,bar,foo,buz", counterWithLabels) - - // Checks that the derived counter value has not changed. - assertPilotCounterValue(t, 1.0, "", rootCounter) - - // Checks that an existing counter (with the same labels) can be incremented. - existingCounterWithLabels := rootCounter.With("foo", "bar").With("foo", "buz") - - existingCounterWithLabels.Add(1) - assertPilotCounterValue(t, 2.0, "foo,bar,foo,buz", existingCounterWithLabels) -} - -func assertPilotCounterValue(t *testing.T, expValue float64, labels string, c metrics.Counter) { - t.Helper() - counter, ok := c.(*pilotCounter).counters.Load(labels) - - require.True(t, ok) - assert.Equal(t, expValue, counter.(*pilotCounter).c.Value()) -} - -func TestPilotGauge(t *testing.T) { - rootGauge := newPilotGauge("rootGauge") - - // Checks that a gauge without labels can be incremented. - rootGauge.Add(1) - - assertPilotGaugeValue(t, 1.0, "", rootGauge) - - // Checks that a gauge (without labels) value can be set. - rootGauge.Set(5.0) - - assertPilotGaugeValue(t, 5.0, "", rootGauge) - - // Checks that a gauge with labels can be incremented. - gaugeWithLabels := rootGauge.With("foo", "bar", "foo", "buz") - gaugeWithLabels.Add(1) - - assertPilotGaugeValue(t, 1.0, "foo,bar,foo,buz", gaugeWithLabels) - - // Checks that the derived gauge value has not changed. - assertPilotGaugeValue(t, 5.0, "", rootGauge) - - // Checks that an existing gauge (with the same labels) can be incremented. - existingGaugeWithLabels := rootGauge.With("foo", "bar").With("foo", "buz") - existingGaugeWithLabels.Add(1) - - assertPilotGaugeValue(t, 2.0, "foo,bar,foo,buz", existingGaugeWithLabels) -} - -func assertPilotGaugeValue(t *testing.T, expValue float64, labels string, g metrics.Gauge) { - t.Helper() - gauge, ok := g.(*pilotGauge).gauges.Load(labels) - - require.True(t, ok) - assert.Equal(t, expValue, gauge.(*pilotGauge).g.Value()) -} - -func TestPilotHistogram(t *testing.T) { - rootHistogram := newPilotHistogram("rootHistogram") - - // Checks that an histogram without labels can be updated. - rootHistogram.Observe(1) - - assertPilotHistogramValues(t, 1.0, 1.0, "", rootHistogram) - - rootHistogram.Observe(2) - - assertPilotHistogramValues(t, 2.0, 3.0, "", rootHistogram) - - // Checks that an histogram with labels can be updated. - histogramWithLabels := rootHistogram.With("foo", "bar", "foo", "buz") - histogramWithLabels.Observe(1) - - assertPilotHistogramValues(t, 1.0, 1.0, "foo,bar,foo,buz", histogramWithLabels) - - // Checks that the derived histogram has not changed. - assertPilotHistogramValues(t, 2.0, 3.0, "", rootHistogram) - - // Checks that an existing histogram (with the same labels) can be updated. - existingHistogramWithLabels := rootHistogram.With("foo", "bar").With("foo", "buz") - existingHistogramWithLabels.Observe(1) - - assertPilotHistogramValues(t, 2.0, 2.0, "foo,bar,foo,buz", existingHistogramWithLabels) -} - -func assertPilotHistogramValues(t *testing.T, expCount, expTotal float64, labels string, h metrics.Histogram) { - t.Helper() - histogram, ok := h.(*pilotHistogram).histograms.Load(labels) - - require.True(t, ok) - assert.Equal(t, expCount, histogram.(*pilotHistogram).count.Value()) - assert.Equal(t, expTotal, histogram.(*pilotHistogram).total.Value()) -} - -func TestPilotMetrics(t *testing.T) { - pilotRegistry := RegisterPilot() - - if !pilotRegistry.IsEpEnabled() || !pilotRegistry.IsSvcEnabled() { - t.Errorf("PilotRegistry should return true for IsEnabled() and IsSvcEnabled()") - } - - pilotRegistry.ConfigReloadsCounter().Add(1) - pilotRegistry.ConfigReloadsFailureCounter().Add(1) - pilotRegistry.LastConfigReloadSuccessGauge().Set(float64(time.Now().Unix())) - pilotRegistry.LastConfigReloadFailureGauge().Set(float64(time.Now().Unix())) - - pilotRegistry. - EntryPointReqsCounter(). - With("code", strconv.Itoa(http.StatusOK), "method", http.MethodGet, "protocol", "http", "entrypoint", "http"). - Add(1) - pilotRegistry. - EntryPointReqDurationHistogram(). - With("code", strconv.Itoa(http.StatusOK), "method", http.MethodGet, "protocol", "http", "entrypoint", "http"). - Observe(1) - pilotRegistry. - EntryPointOpenConnsGauge(). - With("method", http.MethodGet, "protocol", "http", "entrypoint", "http"). - Set(1) - - pilotRegistry. - ServiceReqsCounter(). - With("service", "service1", "code", strconv.Itoa(http.StatusOK), "method", http.MethodGet, "protocol", "http"). - Add(1) - pilotRegistry. - ServiceReqDurationHistogram(). - With("service", "service1", "code", strconv.Itoa(http.StatusOK), "method", http.MethodGet, "protocol", "http"). - Observe(10000) - pilotRegistry. - ServiceOpenConnsGauge(). - With("service", "service1", "method", http.MethodGet, "protocol", "http"). - Set(1) - pilotRegistry. - ServiceRetriesCounter(). - With("service", "service1"). - Add(1) - pilotRegistry. - ServiceServerUpGauge(). - With("service", "service1", "url", "http://127.0.0.10:80"). - Set(1) - - data := pilotRegistry.Data() - - testCases := []struct { - name string - labels map[string]string - assert func(*PilotMetric) - }{ - { - name: pilotConfigReloadsTotalName, - assert: buildPilotCounterAssert(t, pilotConfigReloadsTotalName, 1), - }, - { - name: pilotConfigReloadsFailuresTotalName, - assert: buildPilotCounterAssert(t, pilotConfigReloadsFailuresTotalName, 1), - }, - { - name: pilotConfigLastReloadSuccessName, - assert: buildPilotTimestampAssert(t, pilotConfigLastReloadSuccessName), - }, - { - name: pilotConfigLastReloadFailureName, - assert: buildPilotTimestampAssert(t, pilotConfigLastReloadFailureName), - }, - { - name: pilotEntryPointReqsTotalName, - labels: map[string]string{ - "code": "200", - "method": http.MethodGet, - "protocol": "http", - "entrypoint": "http", - }, - assert: buildPilotCounterAssert(t, pilotEntryPointReqsTotalName, 1), - }, - { - name: pilotEntryPointReqDurationName, - labels: map[string]string{ - "code": "200", - "method": http.MethodGet, - "protocol": "http", - "entrypoint": "http", - }, - assert: buildPilotHistogramAssert(t, pilotEntryPointReqDurationName, 1), - }, - { - name: pilotEntryPointOpenConnsName, - labels: map[string]string{ - "method": http.MethodGet, - "protocol": "http", - "entrypoint": "http", - }, - assert: buildPilotGaugeAssert(t, pilotEntryPointOpenConnsName, 1), - }, - { - name: pilotServiceReqsTotalName, - labels: map[string]string{ - "code": "200", - "method": http.MethodGet, - "protocol": "http", - "service": "service1", - }, - assert: buildPilotCounterAssert(t, pilotServiceReqsTotalName, 1), - }, - { - name: pilotServiceReqDurationName, - labels: map[string]string{ - "code": "200", - "method": http.MethodGet, - "protocol": "http", - "service": "service1", - }, - assert: buildPilotHistogramAssert(t, pilotServiceReqDurationName, 1), - }, - { - name: pilotServiceOpenConnsName, - labels: map[string]string{ - "method": http.MethodGet, - "protocol": "http", - "service": "service1", - }, - assert: buildPilotGaugeAssert(t, pilotServiceOpenConnsName, 1), - }, - { - name: pilotServiceRetriesTotalName, - labels: map[string]string{ - "service": "service1", - }, - assert: buildPilotGreaterThanCounterAssert(t, pilotServiceRetriesTotalName, 1), - }, - { - name: pilotServiceServerUpName, - labels: map[string]string{ - "service": "service1", - "url": "http://127.0.0.10:80", - }, - assert: buildPilotGaugeAssert(t, pilotServiceServerUpName, 1), - }, - } - - for _, test := range testCases { - test := test - t.Run(test.name, func(t *testing.T) { - metric := findPilotMetric(test.name, data) - if metric == nil { - t.Errorf("metrics do not contain %q", test.name) - return - } - - for labels := range metric.Observations { - if len(labels)%2 == 0 { - splitLabels := strings.Split(labels, ",") - for i := 0; i < len(splitLabels); i += 2 { - label := splitLabels[i] - value := splitLabels[i+1] - val, ok := test.labels[label] - if !ok { - t.Errorf("%q metric contains unexpected label %q", test.name, label) - } else if val != value { - t.Errorf("label %q in metric %q has wrong value %q, expected %q", label, test.name, value, val) - } - } - } - } - test.assert(metric) - }) - } -} - -func findPilotMetric(name string, metrics []PilotMetric) *PilotMetric { - for _, metric := range metrics { - if metric.Name == name { - return &metric - } - } - return nil -} - -func buildPilotCounterAssert(t *testing.T, metricName string, expectedValue float64) func(metric *PilotMetric) { - t.Helper() - - return func(metric *PilotMetric) { - for _, value := range metric.Observations { - if cv := value.(float64); cv != expectedValue { - t.Errorf("metric %s has value %f, want %f", metricName, cv, expectedValue) - } - break - } - } -} - -func buildPilotGreaterThanCounterAssert(t *testing.T, metricName string, expectedMinValue float64) func(metric *PilotMetric) { - t.Helper() - - return func(metric *PilotMetric) { - for _, value := range metric.Observations { - if cv := value.(float64); cv < expectedMinValue { - t.Errorf("metric %s has value %f, want at least %f", metricName, cv, expectedMinValue) - } - break - } - } -} - -func buildPilotHistogramAssert(t *testing.T, metricName string, expectedSampleCount float64) func(metric *PilotMetric) { - t.Helper() - - return func(metric *PilotMetric) { - for _, value := range metric.Observations { - if pho := value.(*pilotHistogramObservation); pho.Count != expectedSampleCount { - t.Errorf("metric %s has sample count value %f, want %f", metricName, pho, expectedSampleCount) - } - break - } - } -} - -func buildPilotGaugeAssert(t *testing.T, metricName string, expectedValue float64) func(metric *PilotMetric) { - t.Helper() - - return func(metric *PilotMetric) { - for _, value := range metric.Observations { - if gv := value.(float64); gv != expectedValue { - t.Errorf("metric %s has value %f, want %f", metricName, gv, expectedValue) - } - break - } - } -} - -func buildPilotTimestampAssert(t *testing.T, metricName string) func(metric *PilotMetric) { - t.Helper() - - return func(metric *PilotMetric) { - for _, value := range metric.Observations { - if ts := time.Unix(int64(value.(float64)), 0); time.Since(ts) > time.Minute { - t.Errorf("metric %s has wrong timestamp %v", metricName, ts) - } - break - } - } -} diff --git a/pkg/pilot/pilot.go b/pkg/pilot/pilot.go deleted file mode 100644 index 71a256eb9..000000000 --- a/pkg/pilot/pilot.go +++ /dev/null @@ -1,252 +0,0 @@ -package pilot - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "hash/fnv" - "io" - "net/http" - "time" - - "github.com/cenkalti/backoff/v4" - "github.com/traefik/traefik/v2/pkg/config/dynamic" - "github.com/traefik/traefik/v2/pkg/log" - "github.com/traefik/traefik/v2/pkg/metrics" - "github.com/traefik/traefik/v2/pkg/redactor" - "github.com/traefik/traefik/v2/pkg/safe" - "github.com/traefik/traefik/v2/pkg/version" -) - -const ( - baseInstanceInfoURL = "https://instance-info.pilot.traefik.io/public" - baseGatewayURL = "https://gateway.pilot.traefik.io" -) - -const ( - tokenHeader = "X-Token" - tokenHashHeader = "X-Token-Hash" -) - -const ( - pilotInstanceInfoTimer = 5 * time.Minute - pilotDynConfTimer = 12 * time.Hour - maxElapsedTime = 4 * time.Minute - initialInterval = 5 * time.Second - multiplier = 3 -) - -type instanceInfo struct { - ID string `json:"id,omitempty"` - Metrics []metrics.PilotMetric `json:"metrics,omitempty"` -} - -// New creates a new Pilot. -func New(token string, metricsRegistry *metrics.PilotRegistry, pool *safe.Pool) *Pilot { - tokenHash := fnv.New64a() - - // the `sum64a` implementation of the `Write` method never returns an error. - _, _ = tokenHash.Write([]byte(token)) - - return &Pilot{ - dynamicConfigCh: make(chan dynamic.Configuration), - client: &client{ - token: token, - tokenHash: fmt.Sprintf("%x", tokenHash.Sum64()), - httpClient: &http.Client{Timeout: 5 * time.Second}, - baseInstanceInfoURL: baseInstanceInfoURL, - baseGatewayURL: baseGatewayURL, - }, - routinesPool: pool, - metricsRegistry: metricsRegistry, - } -} - -// Pilot connector with Pilot. -type Pilot struct { - routinesPool *safe.Pool - client *client - - dynamicConfig dynamic.Configuration - dynamicConfigCh chan dynamic.Configuration - metricsRegistry *metrics.PilotRegistry -} - -// SetDynamicConfiguration stores the dynamic configuration. -func (p *Pilot) SetDynamicConfiguration(dynamicConfig dynamic.Configuration) { - p.dynamicConfigCh <- dynamicConfig -} - -func (p *Pilot) sendAnonDynConf(ctx context.Context, config dynamic.Configuration) { - err := p.client.SendAnonDynConf(ctx, config) - if err != nil { - log.WithoutContext().Error(err) - } -} - -func (p *Pilot) sendInstanceInfo(ctx context.Context, pilotMetrics []metrics.PilotMetric) { - err := p.client.SendInstanceInfo(ctx, pilotMetrics) - if err != nil { - log.WithoutContext().Error(err) - } -} - -// Tick sends data periodically. -func (p *Pilot) Tick(ctx context.Context) { - pilotMetrics := p.metricsRegistry.Data() - - p.routinesPool.GoCtx(func(ctxRt context.Context) { - p.sendInstanceInfo(ctxRt, pilotMetrics) - }) - - instanceInfoTicker := time.NewTicker(pilotInstanceInfoTimer) - dynConfTicker := time.NewTicker(pilotDynConfTimer) - - for { - select { - case tick := <-instanceInfoTicker.C: - log.WithoutContext().Debugf("Send instance info to pilot: %s", tick) - - pilotMetrics := p.metricsRegistry.Data() - - p.routinesPool.GoCtx(func(ctxRt context.Context) { - p.sendInstanceInfo(ctxRt, pilotMetrics) - }) - case tick := <-dynConfTicker.C: - log.WithoutContext().Debugf("Send anonymized dynamic configuration to pilot: %s", tick) - - p.routinesPool.GoCtx(func(ctxRt context.Context) { - p.sendAnonDynConf(ctxRt, p.dynamicConfig) - }) - case dynamicConfig := <-p.dynamicConfigCh: - p.dynamicConfig = dynamicConfig - case <-ctx.Done(): - return - } - } -} - -type client struct { - httpClient *http.Client - baseInstanceInfoURL string - baseGatewayURL string - token string - tokenHash string - uuid string -} - -func (c *client) createUUID() (string, error) { - data := []byte(`{"version":"` + version.Version + `","codeName":"` + version.Codename + `"}`) - req, err := http.NewRequest(http.MethodPost, c.baseInstanceInfoURL+"/", bytes.NewBuffer(data)) - if err != nil { - return "", fmt.Errorf("failed to create request: %w", err) - } - - req.Header.Set("Content-Type", "application/json") - req.Header.Set(tokenHeader, c.token) - - resp, err := c.httpClient.Do(req) - if err != nil { - return "", fmt.Errorf("failed call Pilot: %w", err) - } - - defer resp.Body.Close() - - body, err := io.ReadAll(resp.Body) - if err != nil { - return "", fmt.Errorf("failed read response body: %w", err) - } - - if resp.StatusCode/100 != 2 { - return "", fmt.Errorf("wrong status code while sending configuration: %d: %s", resp.StatusCode, body) - } - - created := instanceInfo{} - err = json.Unmarshal(body, &created) - if err != nil { - return "", fmt.Errorf("failed to unmarshal response body: %w", err) - } - - return created.ID, nil -} - -// SendAnonDynConf sends anonymized dynamic configuration to Pilot. -func (c *client) SendAnonDynConf(ctx context.Context, config dynamic.Configuration) error { - anonConfig, err := redactor.Anonymize(&config) - if err != nil { - return fmt.Errorf("unable to anonymize dynamic configuration: %w", err) - } - - req, err := http.NewRequest(http.MethodPost, c.baseGatewayURL+"/collect", bytes.NewReader([]byte(anonConfig))) - if err != nil { - return fmt.Errorf("failed to create request: %w", err) - } - - return c.sendDataRetryable(ctx, req) -} - -// SendInstanceInfo sends instance information to Pilot. -func (c *client) SendInstanceInfo(ctx context.Context, pilotMetrics []metrics.PilotMetric) error { - if len(c.uuid) == 0 { - var err error - c.uuid, err = c.createUUID() - if err != nil { - return fmt.Errorf("failed to create UUID: %w", err) - } - } - - info := instanceInfo{ - ID: c.uuid, - Metrics: pilotMetrics, - } - - b, err := json.Marshal(info) - if err != nil { - return fmt.Errorf("failed to marshall request body: %w", err) - } - - req, err := http.NewRequest(http.MethodPost, c.baseInstanceInfoURL+"/command", bytes.NewReader(b)) - if err != nil { - return fmt.Errorf("failed to create instance info request: %w", err) - } - - req.Header.Set(tokenHeader, c.token) - - return c.sendDataRetryable(ctx, req) -} - -func (c *client) sendDataRetryable(ctx context.Context, req *http.Request) error { - exponentialBackOff := backoff.NewExponentialBackOff() - exponentialBackOff.MaxElapsedTime = maxElapsedTime - exponentialBackOff.InitialInterval = initialInterval - exponentialBackOff.Multiplier = multiplier - - req.Header.Set("Content-Type", "application/json") - req.Header.Set(tokenHashHeader, c.tokenHash) - - return backoff.RetryNotify( - func() error { - resp, err := c.httpClient.Do(req) - if err != nil { - return fmt.Errorf("failed to call Pilot: %w", err) - } - - defer func() { _ = resp.Body.Close() }() - - body, err := io.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("failed to read response body: %w", err) - } - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("wrong status code while sending configuration: %d: %s", resp.StatusCode, body) - } - - return nil - }, - backoff.WithContext(exponentialBackOff, ctx), - func(err error, duration time.Duration) { - log.WithoutContext().Errorf("retry in %s due to: %v ", duration, err) - }) -} diff --git a/pkg/pilot/pilot_test.go b/pkg/pilot/pilot_test.go deleted file mode 100644 index 4076175ca..000000000 --- a/pkg/pilot/pilot_test.go +++ /dev/null @@ -1,225 +0,0 @@ -package pilot - -import ( - "context" - "encoding/json" - "fmt" - "hash/fnv" - "net/http" - "net/http/httptest" - "reflect" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/traefik/traefik/v2/pkg/config/dynamic" - "github.com/traefik/traefik/v2/pkg/metrics" - "github.com/traefik/traefik/v2/pkg/safe" -) - -func TestTick(t *testing.T) { - receivedConfig := make(chan bool) - - mux := http.NewServeMux() - server := httptest.NewServer(mux) - t.Cleanup(server.Close) - - mux.HandleFunc("/", func(rw http.ResponseWriter, req *http.Request) { - if req.Method != http.MethodPost { - http.Error(rw, "invalid method", http.StatusMethodNotAllowed) - return - } - - err := json.NewEncoder(rw).Encode(instanceInfo{ID: "123"}) - if err != nil { - http.Error(rw, err.Error(), http.StatusInternalServerError) - return - } - }) - - mux.HandleFunc("/command", func(rw http.ResponseWriter, req *http.Request) { - if req.Method != http.MethodPost { - http.Error(rw, "invalid method", http.StatusMethodNotAllowed) - return - } - - receivedConfig <- true - }) - - pilot := New("token", metrics.RegisterPilot(), safe.NewPool(context.Background())) - - pilot.client.baseInstanceInfoURL = server.URL - - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - - go pilot.Tick(ctx) - - pilot.SetDynamicConfiguration(dynamic.Configuration{}) - pilot.SetDynamicConfiguration(dynamic.Configuration{}) - - select { - case <-time.Tick(10 * time.Second): - t.Fatal("Timeout") - case <-receivedConfig: - return - } -} - -func TestClient_SendInstanceInfo(t *testing.T) { - myToken := "myToken" - - myTokenHash, err := hashToken(myToken) - require.NoError(t, err) - - mux := http.NewServeMux() - server := httptest.NewServer(mux) - t.Cleanup(server.Close) - - mux.HandleFunc("/", func(rw http.ResponseWriter, req *http.Request) { - if req.Method != http.MethodPost { - http.Error(rw, "invalid method", http.StatusMethodNotAllowed) - return - } - - tk := req.Header.Get(tokenHeader) - if tk != myToken { - http.Error(rw, fmt.Sprintf("invalid token: %s", tk), http.StatusUnauthorized) - return - } - - err := json.NewEncoder(rw).Encode(instanceInfo{ID: "123"}) - if err != nil { - http.Error(rw, err.Error(), http.StatusInternalServerError) - return - } - }) - - mux.HandleFunc("/command", func(rw http.ResponseWriter, req *http.Request) { - if req.Method != http.MethodPost { - http.Error(rw, "invalid method", http.StatusMethodNotAllowed) - return - } - - tk := req.Header.Get(tokenHeader) - if tk != myToken { - http.Error(rw, fmt.Sprintf("invalid token: %s", tk), http.StatusUnauthorized) - return - } - - tkh := req.Header.Get(tokenHashHeader) - if tkh != myTokenHash { - http.Error(rw, fmt.Sprintf("invalid token hash: %s", tkh), http.StatusBadRequest) - return - } - - defer func() { _ = req.Body.Close() }() - - info := &instanceInfo{} - err := json.NewDecoder(req.Body).Decode(info) - if err != nil { - http.Error(rw, err.Error(), http.StatusInternalServerError) - return - } - - if info.ID != "123" { - http.Error(rw, fmt.Sprintf("invalid ID: %s", info.ID), http.StatusBadRequest) - } - }) - - client := client{ - baseInstanceInfoURL: server.URL, - httpClient: http.DefaultClient, - token: myToken, - tokenHash: myTokenHash, - } - - err = client.SendInstanceInfo(context.Background(), []metrics.PilotMetric{}) - require.NoError(t, err) -} - -func TestClient_SendAnonDynConf(t *testing.T) { - myToken := "myToken" - - myTokenHash, err := hashToken(myToken) - require.NoError(t, err) - - var count int - mux := http.NewServeMux() - mux.HandleFunc("/collect", func(rw http.ResponseWriter, req *http.Request) { - count++ - if count == 1 { - http.Error(rw, "OOPS", http.StatusBadRequest) - return - } - - if req.Method != http.MethodPost { - http.Error(rw, "invalid method", http.StatusMethodNotAllowed) - return - } - - tkh := req.Header.Get(tokenHashHeader) - if tkh != myTokenHash { - http.Error(rw, fmt.Sprintf("invalid token hash: %s", tkh), http.StatusBadRequest) - return - } - - defer func() { _ = req.Body.Close() }() - - config := &dynamic.Configuration{} - err := json.NewDecoder(req.Body).Decode(config) - if err != nil { - http.Error(rw, err.Error(), http.StatusInternalServerError) - return - } - - router, exists := config.HTTP.Routers["foo"] - if !exists { - http.Error(rw, "router configuration is missing", http.StatusBadRequest) - return - } - - if !reflect.DeepEqual(router, &dynamic.Router{Service: "foo", Rule: "xxxx"}) { - http.Error(rw, fmt.Sprintf("configuration is not anonymized: %+v", router), http.StatusBadRequest) - return - } - }) - - server := httptest.NewServer(mux) - t.Cleanup(server.Close) - - client := client{ - baseGatewayURL: server.URL, - httpClient: http.DefaultClient, - token: myToken, - tokenHash: myTokenHash, - } - - config := dynamic.Configuration{ - HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{ - "foo": { - Service: "foo", - Rule: "foo.com", - }, - }, - }, - } - - err = client.SendAnonDynConf(context.Background(), config) - require.NoError(t, err) - - assert.Equal(t, 2, count) -} - -func hashToken(token string) (string, error) { - tokenHash := fnv.New64a() - - _, err := tokenHash.Write([]byte(token)) - if err != nil { - return "", err - } - - return fmt.Sprintf("%x", tokenHash.Sum64()), nil -} diff --git a/pkg/plugins/client.go b/pkg/plugins/client.go index 640f67625..831337d93 100644 --- a/pkg/plugins/client.go +++ b/pkg/plugins/client.go @@ -29,20 +29,19 @@ const ( pluginManifest = ".traefik.yml" ) -const pilotURL = "https://plugin.pilot.traefik.io/public/" +const pluginsURL = "https://plugins.traefik.io/public/" const ( hashHeader = "X-Plugin-Hash" tokenHeader = "X-Token" ) -// ClientOptions the options of a Traefik Pilot client. +// ClientOptions the options of a Traefik plugins client. type ClientOptions struct { Output string - Token string } -// Client a Traefik Pilot client. +// Client a Traefik plugins client. type Client struct { HTTPClient *http.Client baseURL *url.URL @@ -54,9 +53,9 @@ type Client struct { sources string } -// NewClient creates a new Traefik Pilot client. +// NewClient creates a new Traefik plugins client. func NewClient(opts ClientOptions) (*Client, error) { - baseURL, err := url.Parse(pilotURL) + baseURL, err := url.Parse(pluginsURL) if err != nil { return nil, err } @@ -87,8 +86,6 @@ func NewClient(opts ClientOptions) (*Client, error) { goPath: goPath, sources: filepath.Join(goPath, goPathSrc), - - token: opts.Token, }, nil } diff --git a/pkg/redactor/redactor_config_test.go b/pkg/redactor/redactor_config_test.go index af7f131c9..1009afba7 100644 --- a/pkg/redactor/redactor_config_test.go +++ b/pkg/redactor/redactor_config_test.go @@ -956,10 +956,6 @@ func TestDo_staticConfiguration(t *testing.T) { }, } - config.Pilot = &static.Pilot{ - Token: "token", - } - config.Experimental = &static.Experimental{ Plugins: map[string]plugins.Descriptor{ "Descriptor0": { diff --git a/pkg/redactor/testdata/anonymized-dynamic-config.json b/pkg/redactor/testdata/anonymized-dynamic-config.json index eba7bd94a..8339f58c3 100644 --- a/pkg/redactor/testdata/anonymized-dynamic-config.json +++ b/pkg/redactor/testdata/anonymized-dynamic-config.json @@ -475,4 +475,4 @@ } } } -} +} \ No newline at end of file diff --git a/pkg/redactor/testdata/anonymized-static-config.json b/pkg/redactor/testdata/anonymized-static-config.json index e159142a4..ec41d040d 100644 --- a/pkg/redactor/testdata/anonymized-static-config.json +++ b/pkg/redactor/testdata/anonymized-static-config.json @@ -445,9 +445,6 @@ } } }, - "pilot": { - "token": "xxxx" - }, "experimental": { "plugins": { "Descriptor0": { diff --git a/pkg/redactor/testdata/secured-dynamic-config.json b/pkg/redactor/testdata/secured-dynamic-config.json index ac6d986e9..352421884 100644 --- a/pkg/redactor/testdata/secured-dynamic-config.json +++ b/pkg/redactor/testdata/secured-dynamic-config.json @@ -483,4 +483,4 @@ } } } -} +} \ No newline at end of file diff --git a/pkg/version/version.go b/pkg/version/version.go index e53d106d0..49b4f9e1e 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -37,11 +37,10 @@ func (v Handler) Append(router *mux.Router) { router.Methods(http.MethodGet).Path("/api/version"). HandlerFunc(func(response http.ResponseWriter, request *http.Request) { v := struct { - Version string - Codename string - StartDate time.Time `json:"startDate"` - UUID string `json:"uuid,omitempty"` - PilotEnabled bool `json:"pilotEnabled"` + Version string + Codename string + StartDate time.Time `json:"startDate"` + UUID string `json:"uuid,omitempty"` }{ Version: Version, Codename: Codename,