diff --git a/CHANGELOG.md b/CHANGELOG.md index 938d3d5e8e..beb6a87bc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,6 +73,8 @@ All notable changes to this project will be documented in this file. - Switch geolocation invocations to `doublezero geolocation ...` and `doublezero init-geolocation-config` - Agent: log after Arista eapi commit - Agent: log received config size in bytes and expose `doublezero_agent_config_size_in_lines` and `doublezero_agent_config_size_in_bytes` Prometheus gauges ([#3741](https://github.com/malbeclabs/doublezero/issues/3741)) +- Controller + - Add `--max-user-tunnel-slots` flag to override the per-device user-tunnel slot count of 128 at runtime. ## [v0.23.0](https://github.com/malbeclabs/doublezero/compare/client/v0.22.0...client/v0.23.0) - 2026-05-15 diff --git a/controlplane/controller/cmd/controller/main.go b/controlplane/controller/cmd/controller/main.go index 3f4571b280..31864fd946 100644 --- a/controlplane/controller/cmd/controller/main.go +++ b/controlplane/controller/cmd/controller/main.go @@ -25,6 +25,7 @@ import ( "github.com/gagliardetto/solana-go" solanarpc "github.com/gagliardetto/solana-go/rpc" "github.com/malbeclabs/doublezero/config" + controllerconfig "github.com/malbeclabs/doublezero/controlplane/controller/config" "github.com/malbeclabs/doublezero/controlplane/controller/internal/controller" pb "github.com/malbeclabs/doublezero/controlplane/proto/controller/gen/pb-go" "github.com/malbeclabs/doublezero/smartcontract/sdk/go/serviceability" @@ -129,24 +130,26 @@ func NewControllerCommand() *ControllerCommand { c.fs.StringVar(&c.tlsKeyFile, "tls-key", "", "path to tls key file") c.fs.BoolVar(&c.enablePprof, "enable-pprof", false, "enable pprof server") c.fs.StringVar(&c.tlsListenPort, "tls-listen-port", "", "listening port for controller grpc server") + c.fs.IntVar(&c.maxUserTunnelSlots, "max-user-tunnel-slots", controllerconfig.DefaultMaxUserTunnelSlots, "per-device user tunnel slot count (must be positive)") return c } type ControllerCommand struct { - fs *flag.FlagSet - description string - listenAddr string - listenPort string - env string - programID string - rpcEndpoint string - deviceLocalASN uint64 - noHardware bool - showVersion bool - tlsCertFile string - tlsKeyFile string - tlsListenPort string - enablePprof bool + fs *flag.FlagSet + description string + listenAddr string + listenPort string + env string + programID string + rpcEndpoint string + deviceLocalASN uint64 + noHardware bool + showVersion bool + tlsCertFile string + tlsKeyFile string + tlsListenPort string + enablePprof bool + maxUserTunnelSlots int } func (c *ControllerCommand) Fs() *flag.FlagSet { @@ -226,6 +229,7 @@ func (c *ControllerCommand) Run() error { defer ledgerRPCClient.Close() options = append(options, controller.WithDeviceLocalASN(deviceLocalASN)) + options = append(options, controller.WithMaxUserTunnelSlots(c.maxUserTunnelSlots)) const defaultFeaturesConfigPath = "/etc/doublezero-controller/features.yaml" if f, err := os.Open(defaultFeaturesConfigPath); err == nil { diff --git a/controlplane/controller/config/constants.go b/controlplane/controller/config/constants.go index 6da2086d39..c12ce10d4a 100644 --- a/controlplane/controller/config/constants.go +++ b/controlplane/controller/config/constants.go @@ -4,6 +4,8 @@ const ( // StartUserTunnelNum is the starting tunnel number for user tunnels StartUserTunnelNum = 500 - // MaxUserTunnelSlots is the maximum number of user tunnel slots per device - MaxUserTunnelSlots = 128 + // DefaultMaxUserTunnelSlots is the default maximum number of user tunnel slots + // per device. Controllers may override this at runtime via the + // --max-user-tunnel-slots flag (see cmd/controller). + DefaultMaxUserTunnelSlots = 128 ) diff --git a/controlplane/controller/internal/controller/models.go b/controlplane/controller/internal/controller/models.go index 1c17c67f5f..9e1d98cfca 100644 --- a/controlplane/controller/internal/controller/models.go +++ b/controlplane/controller/internal/controller/models.go @@ -207,10 +207,10 @@ type Device struct { LocationCode string } -func NewDevice(ip net.IP, publicKey string) *Device { - tunnels := []*Tunnel{} +func NewDevice(ip net.IP, publicKey string, tunnelSlots int) *Device { + tunnels := make([]*Tunnel, 0, tunnelSlots) devicePathologies := []string{} - for i := 0; i < config.MaxUserTunnelSlots; i++ { + for i := 0; i < tunnelSlots; i++ { id := config.StartUserTunnelNum + i tunnel := &Tunnel{ Id: id, @@ -222,7 +222,7 @@ func NewDevice(ip net.IP, publicKey string) *Device { PublicIP: ip, PubKey: publicKey, Tunnels: tunnels, - TunnelSlots: config.MaxUserTunnelSlots, + TunnelSlots: tunnelSlots, DevicePathologies: devicePathologies, } } diff --git a/controlplane/controller/internal/controller/render_test.go b/controlplane/controller/internal/controller/render_test.go index cd4af3a40f..aa1a3040c8 100644 --- a/controlplane/controller/internal/controller/render_test.go +++ b/controlplane/controller/internal/controller/render_test.go @@ -949,7 +949,7 @@ func TestRenderConfig(t *testing.T) { if strings.HasSuffix(test.Want, ".tmpl") { templateData := map[string]int{ "StartTunnel": config.StartUserTunnelNum, - "EndTunnel": config.StartUserTunnelNum + config.MaxUserTunnelSlots - 1, + "EndTunnel": config.StartUserTunnelNum + config.DefaultMaxUserTunnelSlots - 1, } rendered, err := renderTemplateFile(test.Want, templateData) if err != nil { diff --git a/controlplane/controller/internal/controller/server.go b/controlplane/controller/internal/controller/server.go index a3ae0041ba..715b67e1ed 100644 --- a/controlplane/controller/internal/controller/server.go +++ b/controlplane/controller/internal/controller/server.go @@ -18,6 +18,7 @@ import ( "github.com/gogo/protobuf/proto" "github.com/malbeclabs/doublezero/config" + controllerconfig "github.com/malbeclabs/doublezero/controlplane/controller/config" pb "github.com/malbeclabs/doublezero/controlplane/proto/controller/gen/pb-go" telemetryconfig "github.com/malbeclabs/doublezero/controlplane/telemetry/pkg/config" "github.com/malbeclabs/doublezero/smartcontract/sdk/go/serviceability" @@ -40,8 +41,9 @@ const ( ) var ( - ErrServiceabilityRequired = errors.New("serviceability program client is required") - ErrLoggerRequired = errors.New("logger is required") + ErrServiceabilityRequired = errors.New("serviceability program client is required") + ErrLoggerRequired = errors.New("logger is required") + ErrInvalidMaxUserTunnelSlots = errors.New("max user tunnel slots must be positive") ) type ServiceabilityProgramClient interface { @@ -63,29 +65,34 @@ type stateCache struct { type Controller struct { pb.UnimplementedControllerServer - log *slog.Logger - cache stateCache - mu sync.RWMutex - serviceability ServiceabilityProgramClient - listener net.Listener - noHardware bool - updateDone chan struct{} - tlsConfig *tls.Config - environment string - deviceLocalASN uint32 - clickhouse *ClickhouseWriter - featuresConfig *FeaturesConfig + log *slog.Logger + cache stateCache + mu sync.RWMutex + serviceability ServiceabilityProgramClient + listener net.Listener + noHardware bool + updateDone chan struct{} + tlsConfig *tls.Config + environment string + deviceLocalASN uint32 + clickhouse *ClickhouseWriter + featuresConfig *FeaturesConfig + maxUserTunnelSlots int } type Option func(*Controller) func NewController(options ...Option) (*Controller, error) { controller := &Controller{ - cache: stateCache{}, + cache: stateCache{}, + maxUserTunnelSlots: controllerconfig.DefaultMaxUserTunnelSlots, } for _, o := range options { o(controller) } + if controller.maxUserTunnelSlots < 1 { + return nil, fmt.Errorf("%w: got %d", ErrInvalidMaxUserTunnelSlots, controller.maxUserTunnelSlots) + } if controller.listener == nil { lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", 443)) if err != nil { @@ -170,6 +177,12 @@ func WithFeaturesConfig(cfg *FeaturesConfig) Option { } } +func WithMaxUserTunnelSlots(n int) Option { + return func(c *Controller) { + c.maxUserTunnelSlots = n + } +} + // processDeviceInterfacesAndPeers processes a device's interfaces and extracts BGP peer information. // It returns the candidate VPNv4 and IPv4 BGP peers found from the device's loopback interfaces. func (c *Controller) processDeviceInterfacesAndPeers(device serviceability.Device, d *Device, devicePubKey string) (candidateVpnv4BgpPeer, candidateIpv4BgpPeer BgpPeer) { @@ -315,7 +328,7 @@ func (c *Controller) updateStateCache(ctx context.Context) error { } devicePubKey := base58.Encode(device.PubKey[:]) - d := NewDevice(ip, devicePubKey) + d := NewDevice(ip, devicePubKey, c.maxUserTunnelSlots) d.MgmtVrf = device.MgmtVrf d.Code = device.Code diff --git a/controlplane/controller/internal/controller/server_test.go b/controlplane/controller/internal/controller/server_test.go index 011bd21117..c3aba043a3 100644 --- a/controlplane/controller/internal/controller/server_test.go +++ b/controlplane/controller/internal/controller/server_test.go @@ -3,6 +3,7 @@ package controller import ( "bytes" "context" + "errors" "io" "log" "log/slog" @@ -27,7 +28,7 @@ import ( ) // helper that creates a slice of Tunnel structs with sequential IDs. We can use this to populate -// a list of tunnel slots so we don't have to update tests by hand when MaxUserTunnelSlots changes. +// a list of tunnel slots so we don't have to update tests by hand when DefaultMaxUserTunnelSlots changes. func generateEmptyTunnelSlots(startID, count int) []*Tunnel { tunnels := make([]*Tunnel, count) for i := 0; i < count; i++ { @@ -573,7 +574,7 @@ func TestGetConfig(t *testing.T) { if strings.HasSuffix(test.Want, ".tmpl") { templateData := map[string]int{ "StartTunnel": config.StartUserTunnelNum, - "EndTunnel": config.StartUserTunnelNum + config.MaxUserTunnelSlots - 1, + "EndTunnel": config.StartUserTunnelNum + config.DefaultMaxUserTunnelSlots - 1, } rendered, err := renderTemplateFile(test.Want, templateData) if err != nil { @@ -981,8 +982,8 @@ func TestStateCache(t *testing.T) { {239, 0, 0, 1}, }, }, - }, generateEmptyTunnelSlots(config.StartUserTunnelNum+2, config.MaxUserTunnelSlots-2)...), - TunnelSlots: config.MaxUserTunnelSlots, + }, generateEmptyTunnelSlots(config.StartUserTunnelNum+2, config.DefaultMaxUserTunnelSlots-2)...), + TunnelSlots: config.DefaultMaxUserTunnelSlots, Interfaces: []Interface{ { InterfaceType: InterfaceTypePhysical, @@ -1117,8 +1118,8 @@ func TestStateCache(t *testing.T) { MetroRouting: true, TenantPubKey: "11111111111111111111111111111111", }, - }, generateEmptyTunnelSlots(config.StartUserTunnelNum+1, config.MaxUserTunnelSlots-1)...), - TunnelSlots: config.MaxUserTunnelSlots, + }, generateEmptyTunnelSlots(config.StartUserTunnelNum+1, config.DefaultMaxUserTunnelSlots-1)...), + TunnelSlots: config.DefaultMaxUserTunnelSlots, }, }, }, @@ -1231,8 +1232,8 @@ func TestStateCache(t *testing.T) { MetroRouting: true, TenantPubKey: "11111111111111111111111111111111", }, - }, generateEmptyTunnelSlots(config.StartUserTunnelNum+1, config.MaxUserTunnelSlots-1)...), - TunnelSlots: config.MaxUserTunnelSlots, + }, generateEmptyTunnelSlots(config.StartUserTunnelNum+1, config.DefaultMaxUserTunnelSlots-1)...), + TunnelSlots: config.DefaultMaxUserTunnelSlots, }, }, }, @@ -1384,8 +1385,8 @@ func TestStateCache(t *testing.T) { MetroRouting: true, TenantPubKey: "11111111111111111111111111111111", }, - }, generateEmptyTunnelSlots(config.StartUserTunnelNum+2, config.MaxUserTunnelSlots-2)...), - TunnelSlots: config.MaxUserTunnelSlots, + }, generateEmptyTunnelSlots(config.StartUserTunnelNum+2, config.DefaultMaxUserTunnelSlots-2)...), + TunnelSlots: config.DefaultMaxUserTunnelSlots, }, }, }, @@ -1558,8 +1559,8 @@ func TestStateCache(t *testing.T) { MetroRouting: true, TenantPubKey: "7fTN12qMUn1gSUuTMxNCdjndcxwJu45kosXuqJiXMeT9", }, - }, generateEmptyTunnelSlots(config.StartUserTunnelNum+3, config.MaxUserTunnelSlots-3)...), - TunnelSlots: config.MaxUserTunnelSlots, + }, generateEmptyTunnelSlots(config.StartUserTunnelNum+3, config.DefaultMaxUserTunnelSlots-3)...), + TunnelSlots: config.DefaultMaxUserTunnelSlots, Interfaces: []Interface{ { InterfaceType: InterfaceTypeLoopback, @@ -1791,6 +1792,93 @@ func TestServiceabilityProgramClientArg(t *testing.T) { } } +func TestMaxUserTunnelSlotsOption(t *testing.T) { + mockClient := &mockServiceabilityProgramClient{ + ProgramIDFunc: func() solana.PublicKey { + return solana.MustPublicKeyFromBase58("11111111111111111111111111111111") + }, + } + + tests := []struct { + name string + opts []Option + wantErr error + wantSize int + }{ + { + name: "default_when_option_omitted", + opts: nil, + wantErr: nil, + wantSize: config.DefaultMaxUserTunnelSlots, + }, + { + name: "valid_min", + opts: []Option{WithMaxUserTunnelSlots(1)}, + wantErr: nil, + wantSize: 1, + }, + { + name: "valid_default", + opts: []Option{WithMaxUserTunnelSlots(config.DefaultMaxUserTunnelSlots)}, + wantErr: nil, + wantSize: config.DefaultMaxUserTunnelSlots, + }, + { + name: "valid_large", + opts: []Option{WithMaxUserTunnelSlots(1024)}, + wantErr: nil, + wantSize: 1024, + }, + { + name: "invalid_zero", + opts: []Option{WithMaxUserTunnelSlots(0)}, + wantErr: ErrInvalidMaxUserTunnelSlots, + }, + { + name: "invalid_negative", + opts: []Option{WithMaxUserTunnelSlots(-1)}, + wantErr: ErrInvalidMaxUserTunnelSlots, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + opts := []Option{ + WithLogger(slog.New(slog.NewTextHandler(io.Discard, nil))), + WithListener(bufconn.Listen(1024 * 1024)), + WithServiceabilityProgramClient(mockClient), + } + opts = append(opts, test.opts...) + c, err := NewController(opts...) + if test.wantErr != nil { + if !errors.Is(err, test.wantErr) { + t.Fatalf("expected error %v, got %v", test.wantErr, err) + } + return + } + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + if c.maxUserTunnelSlots != test.wantSize { + t.Errorf("expected maxUserTunnelSlots=%d, got %d", test.wantSize, c.maxUserTunnelSlots) + } + d := NewDevice(net.IPv4(1, 2, 3, 4), "pk", c.maxUserTunnelSlots) + if len(d.Tunnels) != test.wantSize { + t.Errorf("expected %d tunnel slots on device, got %d", test.wantSize, len(d.Tunnels)) + } + if d.TunnelSlots != test.wantSize { + t.Errorf("expected device.TunnelSlots=%d, got %d", test.wantSize, d.TunnelSlots) + } + if d.Tunnels[0].Id != config.StartUserTunnelNum { + t.Errorf("expected first tunnel id=%d, got %d", config.StartUserTunnelNum, d.Tunnels[0].Id) + } + if d.Tunnels[test.wantSize-1].Id != config.StartUserTunnelNum+test.wantSize-1 { + t.Errorf("expected last tunnel id=%d, got %d", config.StartUserTunnelNum+test.wantSize-1, d.Tunnels[test.wantSize-1].Id) + } + }) + } +} + // TestEndToEnd verifies on-chain data can be fetched, the local state cache updated, and a config // can be rendered and sent back to the client via gRPC. func TestEndToEnd(t *testing.T) { @@ -2283,7 +2371,7 @@ func TestEndToEnd(t *testing.T) { if strings.HasSuffix(test.Want, ".tmpl") { templateData := map[string]int{ "StartTunnel": config.StartUserTunnelNum, - "EndTunnel": config.StartUserTunnelNum + config.MaxUserTunnelSlots - 1, + "EndTunnel": config.StartUserTunnelNum + config.DefaultMaxUserTunnelSlots - 1, } rendered, err := renderTemplateFile(test.Want, templateData) if err != nil { diff --git a/e2e/ibrl_test.go b/e2e/ibrl_test.go index 28d718b975..a919e78883 100644 --- a/e2e/ibrl_test.go +++ b/e2e/ibrl_test.go @@ -73,7 +73,7 @@ func checkIbgpMsdpPeerRemoved(t *testing.T, dn *TestDevnet, device *devnet.Devic dn.BuildAgentConfigData(t, device.Spec.Code, map[string]any{ "DeviceIP": device.CYOANetworkIP, "StartTunnel": controllerconfig.StartUserTunnelNum, - "EndTunnel": controllerconfig.StartUserTunnelNum + controllerconfig.MaxUserTunnelSlots - 1, + "EndTunnel": controllerconfig.StartUserTunnelNum + controllerconfig.DefaultMaxUserTunnelSlots - 1, })) require.NoError(t, err, "error reading agent configuration fixture for peer removal") err = dn.WaitForAgentConfigMatchViaController(t, device.ID, string(config)) @@ -101,7 +101,7 @@ func checkDeviceDrain(t *testing.T, dn *TestDevnet, device *devnet.Device) { dn.BuildAgentConfigData(t, device.Spec.Code, map[string]any{ "DeviceIP": device.CYOANetworkIP, "StartTunnel": controllerconfig.StartUserTunnelNum, - "EndTunnel": controllerconfig.StartUserTunnelNum + controllerconfig.MaxUserTunnelSlots - 1, + "EndTunnel": controllerconfig.StartUserTunnelNum + controllerconfig.DefaultMaxUserTunnelSlots - 1, })) require.NoError(t, err, "error reading drained config fixture") err = dn.WaitForAgentConfigMatchViaController(t, device.ID, string(config)) @@ -131,7 +131,7 @@ func checkDeviceUndrain(t *testing.T, dn *TestDevnet, device *devnet.Device) { dn.BuildAgentConfigData(t, device.Spec.Code, map[string]any{ "DeviceIP": device.CYOANetworkIP, "StartTunnel": controllerconfig.StartUserTunnelNum, - "EndTunnel": controllerconfig.StartUserTunnelNum + controllerconfig.MaxUserTunnelSlots - 1, + "EndTunnel": controllerconfig.StartUserTunnelNum + controllerconfig.DefaultMaxUserTunnelSlots - 1, })) require.NoError(t, err, "error reading undrained config fixture") err = dn.WaitForAgentConfigMatchViaController(t, device.ID, string(config)) @@ -157,7 +157,7 @@ func checkIBRLPostConnect(t *testing.T, dn *TestDevnet, device *devnet.Device, c "ClientIP": client.CYOANetworkIP, "DeviceIP": device.CYOANetworkIP, "StartTunnel": controllerconfig.StartUserTunnelNum, - "EndTunnel": controllerconfig.StartUserTunnelNum + controllerconfig.MaxUserTunnelSlots - 1, + "EndTunnel": controllerconfig.StartUserTunnelNum + controllerconfig.DefaultMaxUserTunnelSlots - 1, })) require.NoError(t, err, "error reading agent configuration fixture") err = dn.WaitForAgentConfigMatchViaController(t, device.ID, string(config)) @@ -327,7 +327,7 @@ func checkIBRLPostDisconnect(t *testing.T, dn *TestDevnet, device *devnet.Device dn.BuildAgentConfigData(t, device.Spec.Code, map[string]any{ "DeviceIP": device.CYOANetworkIP, "StartTunnel": controllerconfig.StartUserTunnelNum, - "EndTunnel": controllerconfig.StartUserTunnelNum + controllerconfig.MaxUserTunnelSlots - 1, + "EndTunnel": controllerconfig.StartUserTunnelNum + controllerconfig.DefaultMaxUserTunnelSlots - 1, })) require.NoError(t, err, "error reading agent configuration fixture") err = dn.WaitForAgentConfigMatchViaController(t, device.ID, string(config)) diff --git a/e2e/ibrl_with_allocated_ip_test.go b/e2e/ibrl_with_allocated_ip_test.go index 3c5bfbcfa8..20ea8b9c24 100644 --- a/e2e/ibrl_with_allocated_ip_test.go +++ b/e2e/ibrl_with_allocated_ip_test.go @@ -131,7 +131,7 @@ func checkIBRLWithAllocatedIPPostConnect(t *testing.T, dn *TestDevnet, device *d "DeviceIP": device.CYOANetworkIP, "ExpectedAllocatedClientIP": expectedAllocatedClientIP, "StartTunnel": controllerconfig.StartUserTunnelNum, - "EndTunnel": controllerconfig.StartUserTunnelNum + controllerconfig.MaxUserTunnelSlots - 1, + "EndTunnel": controllerconfig.StartUserTunnelNum + controllerconfig.DefaultMaxUserTunnelSlots - 1, })) require.NoError(t, err, "error reading agent configuration fixture") err = dn.WaitForAgentConfigMatchViaController(t, device.ID, string(config)) @@ -323,7 +323,7 @@ func checkIBRLWithAllocatedIPPostDisconnect(t *testing.T, dn *TestDevnet, device dn.BuildAgentConfigData(t, device.Spec.Code, map[string]any{ "DeviceIP": device.CYOANetworkIP, "StartTunnel": controllerconfig.StartUserTunnelNum, - "EndTunnel": controllerconfig.StartUserTunnelNum + controllerconfig.MaxUserTunnelSlots - 1, + "EndTunnel": controllerconfig.StartUserTunnelNum + controllerconfig.DefaultMaxUserTunnelSlots - 1, })) require.NoError(t, err, "error reading agent configuration fixture") err = dn.WaitForAgentConfigMatchViaController(t, device.ID, string(config)) diff --git a/e2e/internal/devnet/device.go b/e2e/internal/devnet/device.go index a3514ee43c..639a553dd9 100644 --- a/e2e/internal/devnet/device.go +++ b/e2e/internal/devnet/device.go @@ -367,8 +367,9 @@ func (d *Device) Start(ctx context.Context) error { } d.log.Debug("--> Created device onchain", "code", spec.Code, "cyoaNetworkIP", cyoaNetworkIP, "dzPrefix", dzPrefix, "devicePK", devicePK) - // MaxUserTunnelSlots is now a constant from config package - d.log.Debug("--> Using MaxUserTunnelSlots constant", "maxUsers", controllerconfig.MaxUserTunnelSlots) + // User tunnel slot count is a per-device default in the controller's config package + // and can be overridden at runtime via the controller's --max-user-tunnel-slots flag. + d.log.Debug("--> Using DefaultMaxUserTunnelSlots", "maxUsers", controllerconfig.DefaultMaxUserTunnelSlots) // Create interfaces onchain. for name, ifaceType := range spec.Interfaces { @@ -868,8 +869,9 @@ func (d *Device) setState(ctx context.Context, containerID string) error { d.ID = onchainID d.CYOANetworkIP = ip - // MaxUserTunnelSlots is now a constant from config package - d.log.Debug("--> Using MaxUserTunnelSlots constant", "maxUsers", controllerconfig.MaxUserTunnelSlots) + // User tunnel slot count is a per-device default in the controller's config package + // and can be overridden at runtime via the controller's --max-user-tunnel-slots flag. + d.log.Debug("--> Using DefaultMaxUserTunnelSlots", "maxUsers", controllerconfig.DefaultMaxUserTunnelSlots) return nil } diff --git a/e2e/multicast_test.go b/e2e/multicast_test.go index 1208909a41..0ed5590172 100644 --- a/e2e/multicast_test.go +++ b/e2e/multicast_test.go @@ -379,7 +379,7 @@ func checkMulticastBothUsersAgentConfig(t *testing.T, dn *TestDevnet, device *de "PublisherTunnelBGPNeighbor": pubTunnelBGPNeighbor, "SubscriberTunnelBGPNeighbor": subTunnelBGPNeighbor, "StartTunnel": controllerconfig.StartUserTunnelNum, - "EndTunnel": controllerconfig.StartUserTunnelNum + controllerconfig.MaxUserTunnelSlots - 1, + "EndTunnel": controllerconfig.StartUserTunnelNum + controllerconfig.DefaultMaxUserTunnelSlots - 1, })) require.NoError(t, err, "error reading agent configuration fixture") err = dn.WaitForAgentConfigMatchViaController(t, device.ID, string(config)) @@ -395,7 +395,7 @@ func checkMulticastBothUsersRemovedAgentConfig(t *testing.T, dn *TestDevnet, dev dn.BuildAgentConfigData(t, device.Spec.Code, map[string]any{ "DeviceIP": device.CYOANetworkIP, "StartTunnel": controllerconfig.StartUserTunnelNum, - "EndTunnel": controllerconfig.StartUserTunnelNum + controllerconfig.MaxUserTunnelSlots - 1, + "EndTunnel": controllerconfig.StartUserTunnelNum + controllerconfig.DefaultMaxUserTunnelSlots - 1, })) require.NoError(t, err, "error reading agent configuration fixture") err = dn.WaitForAgentConfigMatchViaController(t, device.ID, string(config))