mirror of
https://github.com/openziti/ziti.git
synced 2026-10-23 12:15:35 +00:00
a0e53e29d4
- adds a structured xlink.LinkKey ({DialerBinding, Protocol, DestId,
ListenerBinding}) used by all staleness and GC checks, replacing the
pre-rendered key string each side used to pass around
- adds the CheckStaleLinks ctrl message and the `ziti ops verify stale-links`
CLI for operator-driven, two-sided staleness verification with optional --gc,
which acts only when both endpoints agree
- implements auto-GC via the router.link.v1 gcMode (preserve/orphaned/changed):
the router walks its xlinks after each Apply that mutates listeners, dialers,
or gcMode and closes one-sided-stale entries under the configured mode
- sweeps a removal under orphaned whatever the removed config said, including
preserve, since a removal leaves no listeners and no dialers and every
remaining link is one the router can never re-establish
- reports staleness as a three-state verdict (unknown/notStale/stale) so a side
that cannot judge a link abstains instead of guessing; a destination absent
from the registry's listener snapshot means "listeners unknown", not "peer has
none", which keeps a briefly unhealthy peer from losing its live links
- rebuilds the link surface only when a listener or dialer definition actually
moves, so a gcMode-only change no longer rebinds every listen socket
- gates CheckStaleLinks on a new RouterStaleLinkCheck capability and explains
each unqueryable endpoint, including one left unasked by the filter, so a
partial verdict says which of the three causes applies
- detects a peer re-advertising the address a link was dialed to from the
dialing side, not just from the peer's own listener side
- pins every verdict to the link iteration it was computed against, faults that
iteration rather than the current one, and takes the conditional removal's
result as the decision, so a link re-dialed while the sweep was running is
neither closed nor reported as collected
- resolves which side a report speaks for from the sending router against the
controller's link map rather than from the side the report names, so one
router cannot fill both sides and satisfy the both-agree rule --gc depends on
- bounds the query fan-out with a semaphore, matching the other validation
sweeps, since each query carries a minute-long reply timeout
- fails the CLI when the result stream ends before every link is reported, and
drains results already buffered before declaring any missing
- rejects an unrecognized match mode rather than resolving it to changed, the
broadest removal criterion
- consolidates the controller-managed router configuration changelog into one
section, replacing the status that said routers do not yet consume configs at
runtime, and documents the per-config-type opt-in
- restores the Current Beta Features section, dropped when the 2.0 changelog was
archived, and lists controller-managed router configuration in it
- adds unparseable to the codespell ignore list, a spelling variant the
dictionary flags as a typo
For #3743.
144 lines
5.7 KiB
Go
144 lines
5.7 KiB
Go
/*
|
|
Copyright NetFoundry Inc.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
https://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package handler_ctrl
|
|
|
|
import (
|
|
"fmt"
|
|
"runtime/debug"
|
|
"time"
|
|
|
|
"github.com/michaelquigley/pfxlog"
|
|
"github.com/openziti/channel/v5"
|
|
"github.com/openziti/foundation/v2/goroutines"
|
|
"github.com/openziti/ziti/v2/common/capabilities"
|
|
"github.com/openziti/ziti/v2/common/ctrlchan"
|
|
"github.com/openziti/ziti/v2/common/servermetrics"
|
|
"github.com/openziti/ziti/v2/common/trace"
|
|
"github.com/openziti/ziti/v2/router/env"
|
|
"github.com/openziti/ziti/v2/router/forwarder"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
type bindHandler struct {
|
|
env env.RouterEnv
|
|
forwarder *forwarder.Forwarder
|
|
xgDialerPool goroutines.Pool
|
|
terminatorValidationPool goroutines.Pool
|
|
ctrlAddrChangeHandler channel.ContentTypeReceiver
|
|
clusterLeaderChangeHandler channel.ContentTypeReceiver
|
|
}
|
|
|
|
func XgressDialerWorker(_ uint32, f func()) {
|
|
f()
|
|
}
|
|
|
|
func NewBindHandler(routerEnv env.RouterEnv, forwarder *forwarder.Forwarder) (channel.BindHandler, error) {
|
|
xgDialerPoolConfig := goroutines.PoolConfig{
|
|
QueueSize: uint32(forwarder.Options.XgressDial.QueueLength),
|
|
MinWorkers: 0,
|
|
MaxWorkers: uint32(forwarder.Options.XgressDial.WorkerCount),
|
|
IdleTime: 30 * time.Second,
|
|
CloseNotify: routerEnv.GetCloseNotify(),
|
|
PanicHandler: func(err interface{}) {
|
|
pfxlog.Logger().WithField(logrus.ErrorKey, err).WithField("backtrace", string(debug.Stack())).Error("panic during xgress dial")
|
|
},
|
|
WorkerFunction: XgressDialerWorker,
|
|
}
|
|
|
|
servermetrics.ConfigureGoroutinesPoolMetrics(&xgDialerPoolConfig, routerEnv.GetMetricsRegistry(), "pool.route.handler")
|
|
|
|
xgDialerPool, err := goroutines.NewPool(xgDialerPoolConfig)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error creating xgress route handler pool (%w)", err)
|
|
}
|
|
|
|
terminatorValidatorPoolConfig := goroutines.PoolConfig{
|
|
QueueSize: uint32(1),
|
|
MinWorkers: 0,
|
|
MaxWorkers: uint32(50),
|
|
IdleTime: 30 * time.Second,
|
|
CloseNotify: routerEnv.GetCloseNotify(),
|
|
PanicHandler: func(err interface{}) {
|
|
pfxlog.Logger().WithField(logrus.ErrorKey, err).WithField("backtrace", string(debug.Stack())).Error("panic during terminator validation operation")
|
|
},
|
|
WorkerFunction: terminatorValidatorWorker,
|
|
}
|
|
|
|
servermetrics.ConfigureGoroutinesPoolMetrics(&terminatorValidatorPoolConfig, routerEnv.GetMetricsRegistry(), "pool.terminator_validation")
|
|
|
|
terminatorValidationPool, err := goroutines.NewPool(terminatorValidatorPoolConfig)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error creating terminator validation pool (%w)", err)
|
|
}
|
|
|
|
return &bindHandler{
|
|
env: routerEnv,
|
|
forwarder: forwarder,
|
|
xgDialerPool: xgDialerPool,
|
|
terminatorValidationPool: terminatorValidationPool,
|
|
ctrlAddrChangeHandler: newUpdateCtrlAddressesHandler(routerEnv),
|
|
clusterLeaderChangeHandler: newUpdateClusterLeaderHandler(routerEnv),
|
|
}, nil
|
|
}
|
|
|
|
func terminatorValidatorWorker(_ uint32, f func()) {
|
|
f()
|
|
}
|
|
|
|
func (self *bindHandler) BindChannel(binding channel.Binding) error {
|
|
if !capabilities.IsCapable(binding.GetChannel().Underlay().Headers(), capabilities.ControllerSupportsJWTLegacySessions) {
|
|
pfxlog.Logger().WithField("ctrlId", binding.GetChannel().Id()).
|
|
Error("controller does not support JWT format legacy sessions, use with controller versions 2.0+")
|
|
return fmt.Errorf("controller %s does not support JWT format legacy sessions", binding.GetChannel().Id())
|
|
}
|
|
|
|
ctrlCh := binding.GetChannel().GetSenders().(ctrlchan.CtrlChannel)
|
|
channel.AddReceiveHandlers(binding, newPeerStateChangeHandler(self.env))
|
|
channel.AddReceiveHandlers(binding, newRouteHandler(ctrlCh, self.env, self.forwarder, self.xgDialerPool))
|
|
channel.AddReceiveHandlers(binding, newValidateTerminatorsHandler(self.env))
|
|
channel.AddReceiveHandlers(binding, newValidateTerminatorsV2Handler(self.env, self.terminatorValidationPool))
|
|
channel.AddReceiveHandlers(binding, newUnrouteHandler(self.forwarder))
|
|
channel.AddReceiveHandlers(binding, newTraceHandler(self.env.GetRouterId(), self.forwarder.TraceController(), binding.GetChannel()))
|
|
channel.AddReceiveHandlers(binding, self.env.GetInspectHandler())
|
|
channel.AddReceiveHandlers(binding, newCheckStaleLinksHandler(self.env))
|
|
channel.AddReceiveHandlers(binding, newSettingsHandler(self.env))
|
|
channel.AddReceiveHandlers(binding, newFaultHandler(self.env.GetXlinkRegistry()))
|
|
channel.AddReceiveHandlers(binding, self.ctrlAddrChangeHandler)
|
|
channel.AddReceiveHandlers(binding, self.clusterLeaderChangeHandler)
|
|
|
|
binding.AddPeekHandler(trace.NewChannelPeekHandler(self.env.GetRouterId().Token, binding.GetChannel(), self.forwarder.TraceController()))
|
|
|
|
ctrl := self.env.GetNetworkControllers().GetNetworkController(binding.GetChannel().Id())
|
|
if ctrl == nil {
|
|
return fmt.Errorf("controller [%v] not registered, cannot configure", binding.GetChannel().Id())
|
|
}
|
|
|
|
channel.ConfigureHeartbeat(binding, self.env.GetHeartbeatOptions().SendInterval, self.env.GetHeartbeatOptions().CheckInterval, ctrl.HeartbeatCallback())
|
|
|
|
if self.env.GetTraceHandler() != nil {
|
|
binding.AddPeekHandler(self.env.GetTraceHandler())
|
|
}
|
|
|
|
for _, x := range self.env.GetXrctrls() {
|
|
if err := x.BindChannel(binding); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|