diff --git a/.github/workflows/mobile-build-validation.yml b/.github/workflows/mobile-build-validation.yml index 322f129c9..204576d28 100644 --- a/.github/workflows/mobile-build-validation.yml +++ b/.github/workflows/mobile-build-validation.yml @@ -43,8 +43,19 @@ jobs: run: /usr/local/lib/android/sdk/cmdline-tools/7.0/bin/sdkmanager --install "ndk;23.1.7779620" - name: install gomobile run: go install golang.org/x/mobile/cmd/gomobile@v0.0.0-20251113184115-a159579294ab + # `gomobile init` re-installs gobind from golang.org/x/mobile@latest + # regardless of the pin above (cmd/gomobile/init.go: "Make sure gobind is + # up to date"), so this step resolves a version nobody chose, on every run. + # + # setup-go sets GOTOOLCHAIN=local, so that install fails outright once + # x/mobile@latest declares a newer Go than go.mod does — which it did on + # 2026-08-21, breaking both jobs on every branch at once. GOTOOLCHAIN=auto + # lets this one install fetch the toolchain it asks for. Scoped to the + # step: the repo's own Go version, and every build below, is unaffected. - name: gomobile init run: gomobile init + env: + GOTOOLCHAIN: auto - name: build android netbird lib run: PATH=$PATH:$(go env GOPATH) gomobile bind -o $GITHUB_WORKSPACE/netbird.aar -javapkg=io.netbird.gomobile -ldflags="-checklinkname=0 -X golang.zx2c4.com/wireguard/ipc.socketDirectory=/data/data/io.netbird.client/cache/wireguard -X github.com/netbirdio/netbird/version.version=buildtest" $GITHUB_WORKSPACE/client/android env: @@ -64,8 +75,13 @@ jobs: go-version-file: "go.mod" - name: install gomobile run: go install golang.org/x/mobile/cmd/gomobile@v0.0.0-20251113184115-a159579294ab + # See the Android job: `gomobile init` re-installs gobind from + # golang.org/x/mobile@latest regardless of the pin above, and needs a + # toolchain it may pick newer than go.mod's. - name: gomobile init run: gomobile init + env: + GOTOOLCHAIN: auto - name: build iOS netbird lib run: PATH=$PATH:$(go env GOPATH) gomobile bind -target=ios -bundleid=io.netbird.framework -ldflags="-X github.com/netbirdio/netbird/version.version=buildtest" -o ./NetBirdSDK.xcframework ./client/ios/NetBirdSDK env: diff --git a/.github/workflows/no-new-replace.yml b/.github/workflows/no-new-replace.yml new file mode 100644 index 000000000..b906ce450 --- /dev/null +++ b/.github/workflows/no-new-replace.yml @@ -0,0 +1,78 @@ +name: No New Replace Directives + +on: + pull_request: + paths: + - "go.mod" + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref || github.actor_id }} + cancel-in-progress: true + +jobs: + check-replace-directives: + name: check-replace-directives + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + fetch-depth: 0 + + - name: Install Go + uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + with: + go-version-file: go.mod + + - name: Compare replace directives against the base branch + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + run: | + set -euo pipefail + + # A replace directive only applies when this module is the main + # module. Anything importing netbird as a library, the embedded + # clients among them, resolves the replaced path upstream instead and + # fails to build against whatever the replacement provides. Requiring + # a fork under its own module path avoids that; a replace does not. + # + # go.mod is parsed rather than diffed so that reordering, comments and + # single-line versus block syntax do not register as changes. + # + # Versions are part of the key because a replace can be scoped to one + # version of a module. Keyed on paths alone, retargeting such a + # directive at a different version would read as unchanged. + list_replaces() { + go mod edit -json "$1" \ + | jq -r ' + def ref: .Path + (if (.Version // "") == "" then "" else " " + .Version end); + (.Replace // [])[] | "\(.Old | ref) => \(.New | ref)" + ' \ + | sort + } + + git show "${BASE_SHA}:go.mod" > /tmp/base-go.mod + list_replaces /tmp/base-go.mod > /tmp/base-replaces + list_replaces go.mod > /tmp/head-replaces + + added=$(comm -13 /tmp/base-replaces /tmp/head-replaces) + if [ -n "$added" ]; then + echo "::error::This PR adds a replace directive to go.mod:" + echo "$added" | sed 's/^/ /' + echo "" + echo "A replace directive applies only to the main module, so it does not" + echo "reach anything that imports netbird as a library. Require the module" + echo "under a path you control instead, as done for github.com/netbirdio/go-nat." + exit 1 + fi + + removed=$(comm -23 /tmp/base-replaces /tmp/head-replaces) + if [ -n "$removed" ]; then + echo "This PR removes replace directives:" + echo "$removed" | sed 's/^/ /' + fi + echo "No new replace directives." diff --git a/client/android/client.go b/client/android/client.go index a21348f27..7eea83dc0 100644 --- a/client/android/client.go +++ b/client/android/client.go @@ -26,6 +26,8 @@ import ( "github.com/netbirdio/netbird/client/internal/routemanager" "github.com/netbirdio/netbird/client/internal/stdnet" "github.com/netbirdio/netbird/client/net" + "github.com/netbirdio/netbird/client/netstate" + "github.com/netbirdio/netbird/client/netsweep" "github.com/netbirdio/netbird/client/system" "github.com/netbirdio/netbird/formatter" "github.com/netbirdio/netbird/route" @@ -40,11 +42,6 @@ const ( AnonymizeLevelStrict = nbAnonymize.LevelStrictString ) -// ConnectionListener export internal Listener for mobile -type ConnectionListener interface { - peer.Listener -} - // TunAdapter export internal TunAdapter for mobile type TunAdapter interface { device.TunAdapter @@ -85,6 +82,13 @@ type Client struct { deviceName string uiVersion string networkChangeListener listener.NetworkChangeListener + // netState outlives engine restarts: it mirrors the OS connectivity, not + // the engine lifecycle. Run and RunWithoutLogin inject it into each new + // ConnectClient, which distributes it to every reconnection loop. + netState *netstate.State + + // sweeper also outlives engine restarts; NotifyNetworkChange sweeps it. + sweeper *netsweep.Sweeper stateMu sync.RWMutex connectClient *internal.ConnectClient @@ -148,6 +152,7 @@ func NewClient(androidSDKVersion int, deviceName string, uiVersion string, tunAd execWorkaround(androidSDKVersion) net.SetAndroidProtectSocketFn(tunAdapter.ProtectSocket) + system.SetIFaceDiscover(iFaceDiscover) return &Client{ deviceName: deviceName, uiVersion: uiVersion, @@ -156,6 +161,8 @@ func NewClient(androidSDKVersion int, deviceName string, uiVersion string, tunAd recorder: peer.NewRecorder(""), ctxCancelLock: &sync.Mutex{}, networkChangeListener: networkChangeListener, + netState: netstate.New(), + sweeper: netsweep.New(), } } @@ -196,7 +203,8 @@ func (c *Client) Run(platformFiles PlatformFiles, urlOpener URLOpener, isAndroid } // todo do not throw error in case of cancelled context ctx = internal.CtxInitState(ctx) - connectClient := internal.NewConnectClient(ctx, cfg, c.recorder) + connectClient := internal.NewConnectClient(ctx, cfg, c.recorder, + internal.WithNetworkState(c.netState), internal.WithSweeper(c.sweeper)) c.setState(cfg, cacheDir, cfgFile, connectClient) // This path runs the interactive SSO flow, so reaching here means the peer // is authenticated again — release the latch Status() reports from. Clear @@ -237,7 +245,8 @@ func (c *Client) RunWithoutLogin(platformFiles PlatformFiles, dns *DNSList, dnsR // todo do not throw error in case of cancelled context ctx = internal.CtxInitState(ctx) - connectClient := internal.NewConnectClient(ctx, cfg, c.recorder) + connectClient := internal.NewConnectClient(ctx, cfg, c.recorder, + internal.WithNetworkState(c.netState), internal.WithSweeper(c.sweeper)) c.setState(cfg, cacheDir, cfgFile, connectClient) return connectClient.RunOnAndroid(c.tunAdapter, c.iFaceDiscover, c.networkChangeListener, slices.Clone(dns.items), dnsReadyListener, stateFile, cacheDir) } @@ -285,6 +294,24 @@ func (c *Client) GetTunSettings() (*TunSettings, error) { }, nil } +// SetNetworkAvailable feeds OS-reported network availability into the client. +// While unavailable, the internal reconnect loops suspend their attempts and +// the connection listener reports NoNetwork instead of Connecting; when +// availability returns, the loops resume immediately with a fresh backoff. +func (c *Client) SetNetworkAvailable(available bool) { + c.netState.Set(available) + c.recorder.SetNetworkAvailable(available) +} + +// NotifyNetworkChange marks the management, signal and relay connections +// stale after the OS switched networks and schedules a sweep that cuts +// whatever has not redialed on the new network by then. The engine and the +// TUN device stay untouched. +func (c *Client) NotifyNetworkChange() { + c.sweeper.MarkNetworkChange() + log.Infof("network change: connections marked stale") +} + // DebugBundle generates a debug bundle, uploads it, and returns the upload key. // It works both with and without a running engine. anonymizeLevel is "default" // or "strict"; strict also anonymizes internal IP ranges, peer names, and @@ -525,7 +552,11 @@ func (c *Client) OnUpdatedHostDNS(list *DNSList) error { // SetConnectionListener set the network connection listener func (c *Client) SetConnectionListener(listener ConnectionListener) { - c.recorder.SetConnectionListener(listener) + if listener == nil { + c.recorder.RemoveConnectionListener() + return + } + c.recorder.SetConnectionListener(connectionListenerAdapter{listener}) } // RemoveConnectionListener remove connection listener diff --git a/client/android/connection_listener.go b/client/android/connection_listener.go new file mode 100644 index 000000000..77c47574b --- /dev/null +++ b/client/android/connection_listener.go @@ -0,0 +1,41 @@ +//go:build android + +package android + +import ( + "github.com/netbirdio/netbird/client/internal/peer" +) + +// Client state values delivered via ConnectionListener.OnStateChanged, +// re-exported as basic constants so gomobile emits them into the generated +// Java bindings. They mirror peer.ClientState*: append-only, never reorder. +const ( + ClientStateDisconnected = int(peer.ClientStateDisconnected) + ClientStateConnected = int(peer.ClientStateConnected) + ClientStateConnecting = int(peer.ClientStateConnecting) + ClientStateDisconnecting = int(peer.ClientStateDisconnecting) + ClientStateNoNetwork = int(peer.ClientStateNoNetwork) +) + +// ConnectionListener export internal Listener for mobile. It mirrors +// peer.Listener with OnStateChanged taking a plain int (one of the +// ClientState* constants), because gomobile cannot bind named types. +type ConnectionListener interface { + OnStateChanged(state int) + OnConnected() + OnDisconnected() + OnConnecting() + OnDisconnecting() + OnAddressChanged(string, string) + OnPeersListChanged(int) +} + +// connectionListenerAdapter adapts the gomobile-facing ConnectionListener to +// peer.Listener, converting the typed state to the int the binding carries. +type connectionListenerAdapter struct { + ConnectionListener +} + +func (a connectionListenerAdapter) OnStateChanged(state peer.ClientState) { + a.ConnectionListener.OnStateChanged(int(state)) +} diff --git a/client/android/login.go b/client/android/login.go index 897b1561e..24c911eb5 100644 --- a/client/android/login.go +++ b/client/android/login.go @@ -191,40 +191,49 @@ func (a *Auth) login(urlOpener URLOpener, isAndroidTV bool) error { return nil } -// loginHintSetter is implemented by both concrete flows (PKCE and device code) -// but absent from the OAuthFlow interface, hence the assertion below — the same -// way internal/auth wires it in authenticateWithPKCEFlow. -type loginHintSetter interface { - SetLoginHint(hint string) -} - func (a *Auth) foregroundGetTokenInfo(authClient *auth.Auth, urlOpener URLOpener, isAndroidTV bool) (*auth.TokenInfo, error) { - oAuthFlow, err := authClient.GetOAuthFlow(a.ctx, isAndroidTV) + oAuthFlow, err := authClient.GetOAuthFlow(a.ctx, isAndroidTV, profileLoginHint(a.cfgPath)) if err != nil { return nil, fmt.Errorf("failed to get OAuth flow: %v", err) } - // An empty hint is deliberate, not a fallback: a fresh profile leaves the - // choice to the IdP. Switching accounts is done by switching or removing - // profiles, not by logging out — logout keeps the email. - if a.cfgPath != "" { - if hint := readProfileEmail(a.cfgPath); hint != "" { - if setter, ok := oAuthFlow.(loginHintSetter); ok { - setter.SetLoginHint(hint) - } - } + return runOAuthFlow(a.ctx, oAuthFlow, urlOpener, nil) +} + +// profileLoginHint returns the stored account email for the profile at cfgPath. +// An empty hint is deliberate, not a fallback: a fresh profile leaves the +// choice to the IdP. Switching accounts is done by switching or removing +// profiles, not by logging out — logout keeps the email. +func profileLoginHint(cfgPath string) string { + if cfgPath == "" { + return "" + } + return readProfileEmail(cfgPath) +} + +// runOAuthFlow drives an already acquired OAuth flow to a token: requests the +// flow info, presents the verification URL through the opener and waits for +// the browser round-trip. Open is called synchronously — it is what marks the +// surface as opened on the client side, and a fast token's OnLoginSuccess is +// a no-op until it has, so the dismissal would be dropped rather than +// delayed. Openers must therefore not block: they post their UI work and +// return. onWaiting, when set, runs after the URL is shown, right before the +// blocking wait. +func runOAuthFlow(ctx context.Context, flow auth.OAuthFlow, urlOpener URLOpener, onWaiting func()) (*auth.TokenInfo, error) { + flowInfo, err := flow.RequestAuthInfo(ctx) + if err != nil { + return nil, fmt.Errorf("request auth info: %w", err) } - flowInfo, err := oAuthFlow.RequestAuthInfo(context.TODO()) - if err != nil { - return nil, fmt.Errorf("getting a request OAuth flow info failed: %v", err) + urlOpener.Open(flowInfo.VerificationURIComplete, flowInfo.UserCode) + + if onWaiting != nil { + onWaiting() } - go urlOpener.Open(flowInfo.VerificationURIComplete, flowInfo.UserCode) - - tokenInfo, err := oAuthFlow.WaitToken(a.ctx, flowInfo) + tokenInfo, err := flow.WaitToken(ctx, flowInfo) if err != nil { - return nil, fmt.Errorf("waiting for browser login failed: %v", err) + return nil, fmt.Errorf("wait for token: %w", err) } return &tokenInfo, nil diff --git a/client/android/profile_prefs.go b/client/android/profile_prefs.go new file mode 100644 index 000000000..9c1fd307b --- /dev/null +++ b/client/android/profile_prefs.go @@ -0,0 +1,38 @@ +//go:build android + +package android + +import ( + "fmt" + + "github.com/netbirdio/netbird/client/internal/profilemanager" +) + +type prefsStore interface { + Get(namespace string, v any) (bool, error) + Put(namespace string, v any) error +} + +type profilePrefs struct { + prefs *profilemanager.Prefs +} + +func newProfilePrefs(configDir, profileID string) (*profilePrefs, error) { + if configDir == "" || profileID == "" { + return nil, fmt.Errorf("profile prefs require a config dir and profile ID") + } + pm := NewProfileManager(configDir) + prefs, err := pm.serviceMgr.ProfilePrefs(profilemanager.ID(profileID), androidUsername) + if err != nil { + return nil, fmt.Errorf("resolve profile prefs: %w", err) + } + return &profilePrefs{prefs: prefs}, nil +} + +func (p *profilePrefs) Get(namespace string, v any) (bool, error) { + return p.prefs.Get(namespace, v) +} + +func (p *profilePrefs) Put(namespace string, v any) error { + return p.prefs.Put(namespace, v) +} diff --git a/client/android/ssh_client.go b/client/android/ssh_client.go new file mode 100644 index 000000000..2822b6539 --- /dev/null +++ b/client/android/ssh_client.go @@ -0,0 +1,649 @@ +//go:build android + +package android + +import ( + "context" + "errors" + "fmt" + "io" + "net" + "strconv" + "strings" + "sync" + "time" + + log "github.com/sirupsen/logrus" + gossh "golang.org/x/crypto/ssh" + + "github.com/netbirdio/netbird/client/internal" + "github.com/netbirdio/netbird/client/internal/auth" + "github.com/netbirdio/netbird/client/internal/profilemanager" + nbssh "github.com/netbirdio/netbird/client/ssh" + "github.com/netbirdio/netbird/client/ssh/detection" +) + +const ( + sshDialTimeout = 30 * time.Second + sshDetectionTimeout = 5 * time.Second +) + +// PasswordRequiredMarker tells Java to prompt for a password and retry. It is +// a string because gomobile flattens errors to their message, so a sentinel +// value would not survive the binding. +const PasswordRequiredMarker = "netbird-ssh-password-required" + +// HostKeyUnknownMarker tells Java to show the fingerprint and, on confirmation, +// retry with TrustHostKey set. The presented fingerprint is appended after the +// marker so the prompt can display it and the retry can guard against a key +// that changed between the two connects. Only regular (non-NetBird) servers +// reach this: NetBird peers verify against the registry. +const HostKeyUnknownMarker = "netbird-ssh-hostkey-unknown" + +var ( + errPasswordRequired = errors.New(PasswordRequiredMarker) + errClientClosed = errors.New("ssh client closed") +) + +// errHostKeyUnknown carries the presented fingerprint so Connect can build the +// marker message the Java side parses. +type errHostKeyUnknown struct { + fingerprint string +} + +func (e *errHostKeyUnknown) Error() string { + return HostKeyUnknownMarker + ":" + e.fingerprint +} + +// SSHTerminalListener receives SSH session events. It is implemented in Java. +// +// All callbacks are invoked from goroutines and may run concurrently with each +// other; the implementation must be safe to call from any thread. +type SSHTerminalListener interface { + OnConnected() + OnData(data []byte) + OnClose(reason string) + OnError(message string) +} + +// SSHClient is a NetBird-aware SSH client exposed to Java via gomobile. +// +// It dials through the running NetBird tunnel and runs a standard SSH session +// on top with PTY enabled. Host-key verification uses the NetBird-provided +// peer SSH host keys, identical to the desktop client. +type SSHClient struct { + nb *Client + mu sync.Mutex + listener SSHTerminalListener + urlOpener URLOpener + + sshClient *gossh.Client + session *gossh.Session + stdin io.WriteCloser + closed bool + + // gen identifies the current connection attempt. Connect and Close bump it, + // so an in-flight dial or a reader left over from a previous connection + // finds itself stale and stays silent instead of publishing OnConnected or + // OnClose for a connection the caller already abandoned. + gen uint64 + dialCancel context.CancelFunc + + // knownHostsConfigDir and knownHostsProfile locate the TOFU store for + // regular SSH servers in the profile's preferences. Java supplies them, + // since an overlay IP is a different host under a different profile. Empty + // until set: without them a regular server cannot be verified and Connect + // refuses one. + knownHostsConfigDir string + knownHostsProfile string + // trustHostKey carries the fingerprint the user confirmed on a previous + // attempt, so the retry accepts exactly that key and persists it. + trustHostKey string +} + +// NewSSHClient creates a new SSH client bound to the running NetBird Client. +func NewSSHClient(c *Client) *SSHClient { + return &SSHClient{nb: c} +} + +// SetListener registers the Java listener. Must be called before Connect to +// receive any events. +func (s *SSHClient) SetListener(l SSHTerminalListener) { + s.mu.Lock() + s.listener = l + s.mu.Unlock() +} + +// SetURLOpener registers the Java URL opener used to display the device-code +// authorization page in a Custom Tabs window when the target peer requires +// JWT authentication. Must be set before Connect to be effective. +func (s *SSHClient) SetURLOpener(opener URLOpener) { + s.mu.Lock() + s.urlOpener = opener + s.mu.Unlock() +} + +// SetKnownHostsStore points the TOFU host-key store at a profile's preferences. +// Must be set before connecting to a regular SSH server; without it such a +// server cannot be verified and Connect refuses one. +func (s *SSHClient) SetKnownHostsStore(configDir, profileID string) { + s.mu.Lock() + s.knownHostsConfigDir = configDir + s.knownHostsProfile = profileID + s.mu.Unlock() +} + +// TrustHostKey records the fingerprint the user confirmed for a regular server, +// so the next Connect accepts that exact key and adds it to the known-hosts +// store. Passing a fingerprint that no longer matches makes the connect fail +// rather than trust a key that changed since the prompt. +func (s *SSHClient) TrustHostKey(fingerprint string) { + s.mu.Lock() + s.trustHostKey = fingerprint + s.mu.Unlock() +} + +// Connect dials the SSH server through the NetBird tunnel and performs the +// SSH handshake. It auto-detects the server type via SSH banner inspection +// and selects the appropriate authentication path: +// +// - NetBird-SSH server requiring JWT: launches the OAuth 2.0 device-code +// flow, opens the verification URL through the registered URLOpener, and +// uses the resulting token as the SSH password. Host-key verification +// uses the NetBird peer registry. +// - NetBird-SSH server without JWT: authenticates with the NetBird SSH +// private key. Host-key verification uses the NetBird peer registry. +// - Regular SSH server (e.g. OpenSSH): authenticates with the NetBird key +// first (so a user-installed NetBird public key works), then falls back +// to the supplied password if non-empty. Host-key verification is +// trust-on-first-use against the per-profile known-hosts store. +// +// The password parameter is only consulted for regular SSH servers. +func (s *SSHClient) Connect(host string, port int, user, password string) error { + if port < 1 || port > 65535 { + return fmt.Errorf("invalid port: %d", port) + } + + cfg, cfgPath, cc := s.nb.authSnapshot() + if cc == nil { + return errors.New("netbird client not running") + } + if cfg == nil { + return errors.New("netbird config not loaded") + } + engine := cc.Engine() + if engine == nil { + return errors.New("netbird engine not available") + } + + s.mu.Lock() + s.gen++ + gen := s.gen + s.mu.Unlock() + + serverType := detectServerType(host, port) + log.Debugf("SSH server type: %s", serverType) + + authMethods, hostKeyCallback, err := s.buildAuth(cfg, cfgPath, engine, serverType, password) + if err != nil { + return err + } + + clientConfig := &gossh.ClientConfig{ + User: user, + Auth: authMethods, + HostKeyCallback: hostKeyCallback, + Timeout: sshDialTimeout, + } + err = s.dialAndHandshake(gen, host, port, clientConfig) + + // An unknown host key is a prompt, not a failure: return the marker intact + // (rootCause would unwrap it) so Java can show the fingerprint and retry. + var unknownHost *errHostKeyUnknown + if errors.As(err, &unknownHost) { + return errors.New(unknownHost.Error()) + } + + // A regular server may still accept a password, so let the caller ask for + // one instead of failing. NetBird servers never use a password, so a + // failure there is genuine. + if err != nil && serverType != detection.ServerTypeNetBirdJWT && + serverType != detection.ServerTypeNetBirdNoJWT && isAuthFailure(err) && + passwordCouldHelp(err, password != "") { + return errPasswordRequired + } + if err != nil { + return rootCause(err) + } + return nil +} + +// StartSession requests a PTY and starts an interactive shell. Output from +// the session is forwarded to the listener via OnData. +func (s *SSHClient) StartSession(cols, rows int) error { + err := s.startSession(cols, rows) + if err != nil { + log.Infof("SSH: start session failed: %v", err) + return rootCause(err) + } + return nil +} + +// Write sends data to the SSH session stdin. +func (s *SSHClient) Write(data []byte) error { + s.mu.Lock() + stdin := s.stdin + s.mu.Unlock() + if stdin == nil { + return errors.New("ssh session not started") + } + if _, err := stdin.Write(data); err != nil { + return fmt.Errorf("write stdin: %w", err) + } + return nil +} + +// Resize updates the PTY window size. +func (s *SSHClient) Resize(cols, rows int) error { + s.mu.Lock() + session := s.session + s.mu.Unlock() + if session == nil { + return errors.New("ssh session not started") + } + return session.WindowChange(rows, cols) +} + +// Reset makes a closed client usable for another Connect: Close leaves the +// one-shot guard set, and clearing it lets the same client back a reconnect. +func (s *SSHClient) Reset() { + s.mu.Lock() + defer s.mu.Unlock() + s.closed = false +} + +// Close terminates the SSH session and underlying connection. Safe to call +// multiple times. +func (s *SSHClient) Close() error { + s.mu.Lock() + s.gen++ + if s.dialCancel != nil { + s.dialCancel() + s.dialCancel = nil + } + sshClient := s.sshClient + session := s.session + stdin := s.stdin + s.sshClient = nil + s.session = nil + s.stdin = nil + notify := !s.closed + s.closed = true + listener := s.listener + s.mu.Unlock() + + if stdin != nil { + if err := stdin.Close(); err != nil { + log.Debugf("ssh: stdin close: %v", err) + } + } + if session != nil { + if err := session.Close(); err != nil && !errors.Is(err, io.EOF) { + log.Debugf("ssh: session close: %v", err) + } + } + var firstErr error + if sshClient != nil { + if err := sshClient.Close(); err != nil { + firstErr = err + } + } + if notify && listener != nil { + listener.OnClose("closed by client") + } + return firstErr +} + +func (s *SSHClient) startSession(cols, rows int) error { + log.Debugf("SSH: starting session %dx%d", cols, rows) + s.mu.Lock() + sshClient := s.sshClient + gen := s.gen + s.mu.Unlock() + + if sshClient == nil { + return errors.New("ssh client not connected") + } + + pty, err := nbssh.StartPTYSession(sshClient, cols, rows) + if err != nil { + return err + } + + s.mu.Lock() + if gen != s.gen { + s.mu.Unlock() + closeQuiet(pty.Session, "stale session") + return errClientClosed + } + s.session = pty.Session + s.stdin = pty.Stdin + s.mu.Unlock() + + readerDone := make(chan string, 2) + go func() { readerDone <- s.readLoop(pty.Stdout, "stdout") }() + go func() { readerDone <- s.readLoop(pty.Stderr, "stderr") }() + go func() { + reason := <-readerDone + if second := <-readerDone; reason == "" { + reason = second + } + s.notifyClose(gen, reason) + }() + log.Debug("SSH: session started, shell running") + return nil +} + +func (s *SSHClient) buildAuth(cfg *profilemanager.Config, cfgPath string, engine *internal.Engine, + serverType detection.ServerType, password string) ([]gossh.AuthMethod, gossh.HostKeyCallback, error) { + + switch serverType { + case detection.ServerTypeNetBirdJWT: + token, err := s.requestJWTToken(cfg, cfgPath) + if err != nil { + return nil, nil, fmt.Errorf("jwt: %w", err) + } + auths := []gossh.AuthMethod{gossh.Password(token)} + return auths, nbssh.CreateHostKeyCallback(nbssh.PeerKeyLookup(engine.GetPeerSSHKey)), nil + + case detection.ServerTypeNetBirdNoJWT: + if cfg.SSHKey == "" { + return nil, nil, errors.New("no NetBird SSH key available") + } + signer, err := gossh.ParsePrivateKey([]byte(cfg.SSHKey)) + if err != nil { + return nil, nil, fmt.Errorf("parse netbird ssh key: %w", err) + } + auths := []gossh.AuthMethod{gossh.PublicKeys(signer)} + return auths, nbssh.CreateHostKeyCallback(nbssh.PeerKeyLookup(engine.GetPeerSSHKey)), nil + + case detection.ServerTypeRegular: + var auths []gossh.AuthMethod + if cfg.SSHKey != "" { + if signer, err := gossh.ParsePrivateKey([]byte(cfg.SSHKey)); err == nil { + auths = append(auths, gossh.PublicKeys(signer)) + } else { + log.Debugf("ssh: parse netbird key for regular auth: %v", err) + } + } + if password != "" { + pw := password + auths = append(auths, gossh.Password(pw)) + auths = append(auths, gossh.KeyboardInteractive(func(_, _ string, questions []string, _ []bool) ([]string, error) { + answers := make([]string, len(questions)) + for i := range questions { + answers[i] = pw + } + return answers, nil + })) + } + if len(auths) == 0 { + // Nothing to offer at all: ask for a password rather than failing, + // so the caller can retry once the user supplies one. + return nil, nil, errPasswordRequired + } + callback, err := s.tofuHostKeyCallback() + if err != nil { + return nil, nil, err + } + return auths, callback, nil + + default: + return nil, nil, fmt.Errorf("unsupported SSH server type: %v", serverType) + } +} + +// tofuHostKeyCallback verifies a regular server's host key against the +// per-profile known-hosts store. An unknown host returns errHostKeyUnknown so +// Java can show the fingerprint and, once confirmed, retry with the key +// trusted; a changed key is rejected outright, as OpenSSH does. When the user +// has confirmed a fingerprint, the callback accepts exactly that key and +// appends it to the store. +func (s *SSHClient) tofuHostKeyCallback() (gossh.HostKeyCallback, error) { + s.mu.Lock() + configDir := s.knownHostsConfigDir + profileID := s.knownHostsProfile + trusted := s.trustHostKey + s.mu.Unlock() + + if configDir == "" || profileID == "" { + return nil, errors.New("no known-hosts store configured for regular SSH") + } + + store, err := openKnownHostsStore(configDir, profileID) + if err != nil { + return nil, fmt.Errorf("load known-hosts store: %w", err) + } + + return func(hostname string, remote net.Addr, key gossh.PublicKey) error { + verdict, err := store.verify(hostname, remote, key) + if err != nil { + return err + } + if verdict == hostKeyMatched { + return nil + } + if verdict == hostKeyChanged { + return fmt.Errorf("SSH host key changed for %s (possible attack)", hostname) + } + + fingerprint := gossh.FingerprintSHA256(key) + if trusted == "" { + return &errHostKeyUnknown{fingerprint: fingerprint} + } + if trusted != fingerprint { + return fmt.Errorf("SSH host key changed since it was confirmed for %s", hostname) + } + if err := store.append(hostname, remote, key); err != nil { + return fmt.Errorf("persist trusted host key: %w", err) + } + // The confirmation is spent: now that the key is stored, a later + // reconnect must verify against the file, not re-accept this fingerprint. + s.mu.Lock() + s.trustHostKey = "" + s.mu.Unlock() + return nil + }, nil +} + +func (s *SSHClient) requestJWTToken(cfg *profilemanager.Config, cfgPath string) (string, error) { + s.mu.Lock() + urlOpener := s.urlOpener + s.mu.Unlock() + if urlOpener == nil { + return "", errors.New("URL opener not configured for JWT auth") + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer cancel() + + flow, err := auth.NewOAuthFlow(ctx, cfg, false, true, profileLoginHint(cfgPath)) + if err != nil { + return "", fmt.Errorf("create oauth flow: %w", err) + } + + // The status callback covers the browser round-trip, which would + // otherwise leave the terminal blank. + tokenInfo, err := runOAuthFlow(ctx, flow, urlOpener, func() { + s.notifyStatus("Waiting for browser authentication...") + }) + if err != nil { + return "", err + } + + token := tokenInfo.GetTokenToUse() + if token == "" { + return "", errors.New("empty token returned by IdP") + } + + // Tells the client the browser round-trip is over so it can dismiss the + // surface it opened, the same way the login and session-extend flows do. + // Without it the Custom Tab stays in front of the terminal even though the + // token has already been collected. + urlOpener.OnLoginSuccess() + + return token, nil +} + +func (s *SSHClient) dialAndHandshake(gen uint64, host string, port int, clientConfig *gossh.ClientConfig) error { + addr := net.JoinHostPort(host, strconv.Itoa(port)) + ctx, cancel := context.WithTimeout(context.Background(), sshDialTimeout) + defer cancel() + + s.mu.Lock() + if gen != s.gen { + s.mu.Unlock() + return errClientClosed + } + s.dialCancel = cancel + s.mu.Unlock() + + var dialer net.Dialer + conn, err := dialer.DialContext(ctx, "tcp", addr) + if err != nil { + return fmt.Errorf("dial %s: %w", addr, err) + } + + client, err := nbssh.Handshake(ctx, conn, addr, clientConfig) + if err != nil { + return err + } + + s.mu.Lock() + if gen != s.gen { + s.mu.Unlock() + closeQuiet(client, "stale ssh client") + return errClientClosed + } + s.sshClient = client + listener := s.listener + s.mu.Unlock() + + if listener != nil { + listener.OnConnected() + } + return nil +} + +func (s *SSHClient) readLoop(r io.Reader, name string) string { + buf := make([]byte, 4096) + for { + n, err := r.Read(buf) + if n > 0 { + s.mu.Lock() + listener := s.listener + s.mu.Unlock() + if listener != nil { + chunk := make([]byte, n) + copy(chunk, buf[:n]) + listener.OnData(chunk) + } + } + if err != nil { + // EOF is a normal shell exit, so report it without a reason. + if errors.Is(err, io.EOF) { + return "" + } + log.Debugf("ssh %s read: %v", name, err) + return rootCause(err).Error() + } + } +} + +// notifyStatus writes a progress line to the terminal through the normal +// output path, so long steps are visible while nothing else is arriving. +func (s *SSHClient) notifyStatus(text string) { + s.mu.Lock() + listener := s.listener + s.mu.Unlock() + if listener != nil { + listener.OnData([]byte("\r\n\x1b[33m" + text + "\x1b[0m\r\n")) + } +} + +func (s *SSHClient) notifyClose(gen uint64, reason string) { + s.mu.Lock() + if gen != s.gen || s.closed { + s.mu.Unlock() + return + } + s.closed = true + listener := s.listener + s.mu.Unlock() + if listener != nil { + listener.OnClose(reason) + } +} + +func closeQuiet(c io.Closer, label string) { + if c == nil { + return + } + if err := c.Close(); err != nil && !errors.Is(err, io.EOF) { + log.Debugf("ssh: close %s: %v", label, err) + } +} + +func detectServerType(host string, port int) detection.ServerType { + ctx, cancel := context.WithTimeout(context.Background(), sshDetectionTimeout) + defer cancel() + + dialer := &net.Dialer{} + serverType, err := detection.DetectSSHServerType(ctx, dialer, host, port) + if err != nil { + log.Debugf("ssh: server detection failed: %v (assuming regular SSH)", err) + return detection.ServerTypeRegular + } + return serverType +} + +// rootCause returns the innermost error of a %w chain, so the terminal shows +// "i/o timeout" rather than every layer that added context on the way up. +func rootCause(err error) error { + for { + // A joined error has no single root, so keep it as-is. + if _, ok := err.(interface{ Unwrap() []error }); ok { + return err + } + next := errors.Unwrap(err) + if next == nil { + return err + } + err = next + } +} + +// isAuthFailure distinguishes credential rejection from dial, timeout and +// host-key errors, which retrying with a password would not fix. +func isAuthFailure(err error) bool { + if errors.Is(err, errPasswordRequired) { + return true + } + var partial *gossh.PartialSuccessError + if errors.As(err, &partial) { + return true + } + return strings.Contains(err.Error(), "unable to authenticate") +} + +// passwordCouldHelp reports whether prompting for a password again can change +// the outcome. gossh lists a method under "attempted methods" only when the +// server offered it, so a supplied password that was never attempted means the +// server does not accept passwords and the real error should surface instead. +func passwordCouldHelp(err error, passwordOffered bool) bool { + if !passwordOffered { + return true + } + msg := err.Error() + return strings.Contains(msg, "password") || strings.Contains(msg, "keyboard-interactive") +} diff --git a/client/android/ssh_known_hosts.go b/client/android/ssh_known_hosts.go new file mode 100644 index 000000000..eea90fd32 --- /dev/null +++ b/client/android/ssh_known_hosts.go @@ -0,0 +1,168 @@ +//go:build android + +package android + +import ( + "bytes" + "net" + "strconv" + "strings" + "sync" + + gossh "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/knownhosts" +) + +const knownHostsNamespace = "ssh" + +const ( + hostKeyUnknown hostKeyVerdict = iota + hostKeyMatched + hostKeyChanged +) + +var knownHostsMu sync.Mutex + +type hostKeyVerdict uint8 + +type knownHostsSection struct { + KnownHosts []string `json:"knownHosts"` +} + +type knownHostsStore struct { + prefs prefsStore +} + +// RemoveKnownHost deletes every known-hosts entry for host:port from the +// profile's store, so a host trusted for a session that is being deleted does +// not linger. Java calls this only once no session targets that host, so a +// shared host stays trusted. A missing entry is not an error: the goal state +// is "absent". +func RemoveKnownHost(configDir, profileID, host string, port int) error { + store, err := openKnownHostsStore(configDir, profileID) + if err != nil { + return err + } + return store.removeHost(host, port) +} + +func openKnownHostsStore(configDir, profileID string) (*knownHostsStore, error) { + prefs, err := newProfilePrefs(configDir, profileID) + if err != nil { + return nil, err + } + return &knownHostsStore{prefs: prefs}, nil +} + +func (st *knownHostsStore) verify(hostname string, remote net.Addr, key gossh.PublicKey) (hostKeyVerdict, error) { + lines, err := st.lines() + if err != nil { + return hostKeyUnknown, err + } + targets := knownHostsTargets(hostname, remote) + + verdict := hostKeyUnknown + for _, line := range lines { + pubKey, ok := knownHostsLineKey(line, targets) + if !ok { + continue + } + if pubKey.Type() == key.Type() && bytes.Equal(pubKey.Marshal(), key.Marshal()) { + return hostKeyMatched, nil + } + verdict = hostKeyChanged + } + return verdict, nil +} + +func (st *knownHostsStore) append(hostname string, remote net.Addr, key gossh.PublicKey) error { + line := knownhosts.Line(knownHostsTargets(hostname, remote), key) + + knownHostsMu.Lock() + defer knownHostsMu.Unlock() + + lines, err := st.lines() + if err != nil { + return err + } + return st.prefs.Put(knownHostsNamespace, knownHostsSection{KnownHosts: append(lines, line)}) +} + +func (st *knownHostsStore) removeHost(host string, port int) error { + target := knownhosts.Normalize(net.JoinHostPort(host, strconv.Itoa(port))) + + knownHostsMu.Lock() + defer knownHostsMu.Unlock() + + lines, err := st.lines() + if err != nil { + return err + } + kept := make([]string, 0, len(lines)) + for _, line := range lines { + if knownHostsLineMatches(line, target) { + continue + } + kept = append(kept, line) + } + if len(kept) == len(lines) { + return nil + } + return st.prefs.Put(knownHostsNamespace, knownHostsSection{KnownHosts: kept}) +} + +func (st *knownHostsStore) lines() ([]string, error) { + var section knownHostsSection + if _, err := st.prefs.Get(knownHostsNamespace, §ion); err != nil { + return nil, err + } + return section.KnownHosts, nil +} + +func knownHostsTargets(hostname string, remote net.Addr) []string { + targets := []string{knownhosts.Normalize(hostname)} + if remote != nil { + if normalized := knownhosts.Normalize(remote.String()); normalized != targets[0] { + targets = append(targets, normalized) + } + } + return targets +} + +func knownHostsLineKey(line string, targets []string) (gossh.PublicKey, bool) { + trimmed := strings.TrimSpace(line) + if trimmed == "" || strings.HasPrefix(trimmed, "#") { + return nil, false + } + _, hosts, pubKey, _, _, err := gossh.ParseKnownHosts([]byte(trimmed)) + if err != nil { + return nil, false + } + for _, host := range hosts { + for _, target := range targets { + if host == target { + return pubKey, true + } + } + } + return nil, false +} + +// knownHostsLineMatches reports whether a known-hosts line's address list +// contains the normalized target. Comment and blank lines never match. +func knownHostsLineMatches(line, target string) bool { + trimmed := strings.TrimSpace(line) + if trimmed == "" || strings.HasPrefix(trimmed, "#") { + return false + } + fields := strings.Fields(trimmed) + if len(fields) == 0 { + return false + } + for _, addr := range strings.Split(fields[0], ",") { + if addr == target { + return true + } + } + return false +} diff --git a/client/android/ssh_sessions.go b/client/android/ssh_sessions.go new file mode 100644 index 000000000..44b5464e9 --- /dev/null +++ b/client/android/ssh_sessions.go @@ -0,0 +1,104 @@ +//go:build android + +package android + +const ( + sshSessionsNamespace = "ssh-sessions" + maxStoredSSHSessions = 50 +) + +type sshSessionRecord struct { + ID string `json:"id"` + Host string `json:"host"` + Port int `json:"port"` + User string `json:"user"` +} + +type sshSessionsSection struct { + Sessions []sshSessionRecord `json:"sessions"` +} + +// SSHSessionEntry is one stored SSH session, without any credential. +type SSHSessionEntry struct { + ID string + Host string + Port int + User string +} + +// SSHSessionArray wraps stored SSH sessions for gomobile compatibility. +type SSHSessionArray struct { + items []*SSHSessionEntry +} + +// NewSSHSessionArray creates an empty session array to fill via Add. +func NewSSHSessionArray() *SSHSessionArray { + return &SSHSessionArray{} +} + +// Add appends a session entry, oldest first. +func (a *SSHSessionArray) Add(id, host string, port int, user string) { + a.items = append(a.items, &SSHSessionEntry{ID: id, Host: host, Port: port, User: user}) +} + +// Length returns the number of entries. +func (a *SSHSessionArray) Length() int { + return len(a.items) +} + +// Get returns the entry at index i, or nil when out of range. +func (a *SSHSessionArray) Get(i int) *SSHSessionEntry { + if i < 0 || i >= len(a.items) { + return nil + } + return a.items[i] +} + +// SSHSessionStore reads and writes a profile's stored SSH sessions. +type SSHSessionStore struct { + prefs prefsStore +} + +// NewSSHSessionStore opens the session store of the given profile. +func NewSSHSessionStore(configDir, profileID string) (*SSHSessionStore, error) { + prefs, err := newProfilePrefs(configDir, profileID) + if err != nil { + return nil, err + } + return &SSHSessionStore{prefs: prefs}, nil +} + +// Load returns the stored sessions, oldest first. +func (s *SSHSessionStore) Load() (*SSHSessionArray, error) { + var section sshSessionsSection + if _, err := s.prefs.Get(sshSessionsNamespace, §ion); err != nil { + return nil, err + } + + out := NewSSHSessionArray() + for _, record := range section.Sessions { + if record.ID == "" || record.Host == "" { + continue + } + out.Add(record.ID, record.Host, record.Port, record.User) + } + return out, nil +} + +// Save replaces the stored sessions, keeping only the newest entries when the +// list exceeds the storage cap. +func (s *SSHSessionStore) Save(sessions *SSHSessionArray) error { + var items []*SSHSessionEntry + if sessions != nil { + items = sessions.items + } + if len(items) > maxStoredSSHSessions { + items = items[len(items)-maxStoredSSHSessions:] + } + + records := make([]sshSessionRecord, 0, len(items)) + for _, item := range items { + records = append(records, sshSessionRecord{ID: item.ID, Host: item.Host, Port: item.Port, User: item.User}) + } + return s.prefs.Put(sshSessionsNamespace, sshSessionsSection{Sessions: records}) +} diff --git a/client/cmd/service_controller.go b/client/cmd/service_controller.go index 9ba3bce25..b187a7b87 100644 --- a/client/cmd/service_controller.go +++ b/client/cmd/service_controller.go @@ -45,8 +45,8 @@ func daemonServerOptions(network string) []grpc.ServerOption { return nil } - creds := ipcauth.NewTransportCredentials() - if creds == nil { + creds := ipcauth.NewTransportCredentials() //nolint:staticcheck + if creds == nil { //nolint:staticcheck // nil only on platforms without a peer-identity primitive log.Warnf("daemon IPC has no peer-identity primitive on %s: privileged operations will be denied", runtime.GOOS) return nil } diff --git a/client/cmd/service_socket.go b/client/cmd/service_socket.go index ed1f001a7..bf3122f7c 100644 --- a/client/cmd/service_socket.go +++ b/client/cmd/service_socket.go @@ -27,8 +27,8 @@ func listenOnAddress(addr string) (*socketListener, error) { } if network == "npipe" { - listener, path, err := listenNamedPipe(address) - if err != nil { + listener, path, err := listenNamedPipe(address) //nolint:staticcheck + if err != nil { //nolint:staticcheck // always errors on non-Windows builds return nil, err } return &socketListener{Listener: listener, network: network, address: path}, nil diff --git a/client/cmd/testutil_test.go b/client/cmd/testutil_test.go index 205327ef5..f40056f83 100644 --- a/client/cmd/testutil_test.go +++ b/client/cmd/testutil_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel" "google.golang.org/grpc" diff --git a/client/embed/embed.go b/client/embed/embed.go index 99a6b8229..079e03c63 100644 --- a/client/embed/embed.go +++ b/client/embed/embed.go @@ -21,7 +21,7 @@ import ( "github.com/netbirdio/netbird/client/internal/auth" "github.com/netbirdio/netbird/client/internal/peer" "github.com/netbirdio/netbird/client/internal/profilemanager" - sshcommon "github.com/netbirdio/netbird/client/ssh" + nbssh "github.com/netbirdio/netbird/client/ssh" "github.com/netbirdio/netbird/client/system" "github.com/netbirdio/netbird/shared/management/domain" mgmProto "github.com/netbirdio/netbird/shared/management/proto" @@ -91,6 +91,13 @@ type Options struct { // when the embedded client must never act as a stepping stone into // the host's local network (e.g. the proxy's overlay peer). BlockLANAccess bool + // LazyConnectionEnabled is a tri-state local override for lazy connections, + // mirroring the NB_LAZY_CONN env var. Nil defers to the management feature + // flag; a set value overrides it in both directions. A short-lived client + // that reaches only a few known peers can set this to false, so its peers + // connect eagerly and the first request does not wait for the connection to + // be established. + LazyConnectionEnabled *bool // WireguardPort is the port for the tunnel interface. Use 0 for a random port. WireguardPort *int // MTU is the MTU for the tunnel interface. @@ -220,6 +227,15 @@ func New(opts Options) (*Client, error) { config.PrivateKey = opts.PrivateKey } + if opts.LazyConnectionEnabled != nil { + // Runtime-only override, read back through lazyconn.ParseState; a set value + // wins over the management feature flag in both directions. + config.LazyConnection = "off" + if *opts.LazyConnectionEnabled { + config.LazyConnection = "on" + } + } + if opts.Performance.PreallocatedBuffersPerPool != nil { wgdevice.SetPreallocatedBuffersPerPool(*opts.Performance.PreallocatedBuffersPerPool) } @@ -521,12 +537,7 @@ func (c *Client) VerifySSHHostKey(peerAddress string, key []byte) error { return err } - storedKey, found := engine.GetPeerSSHKey(peerAddress) - if !found { - return sshcommon.ErrPeerNotFound - } - - return sshcommon.VerifyHostKey(storedKey, key, peerAddress) + return nbssh.PeerKeyLookup(engine.GetPeerSSHKey).VerifySSHHostKey(peerAddress, key) } // SetPerformance retunes a running Client. Only PreallocatedBuffersPerPool diff --git a/client/embed/embed_test.go b/client/embed/embed_test.go index a2f438975..27beb8934 100644 --- a/client/embed/embed_test.go +++ b/client/embed/embed_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/require" "google.golang.org/grpc" diff --git a/client/firewall/uspfilter/filter_filter_test.go b/client/firewall/uspfilter/filter_filter_test.go index a64c83138..5ca8538be 100644 --- a/client/firewall/uspfilter/filter_filter_test.go +++ b/client/firewall/uspfilter/filter_filter_test.go @@ -5,7 +5,7 @@ import ( "net/netip" "testing" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/stretchr/testify/require" diff --git a/client/firewall/uspfilter/filter_routeacl_test.go b/client/firewall/uspfilter/filter_routeacl_test.go index 449554d8b..b6397d09b 100644 --- a/client/firewall/uspfilter/filter_routeacl_test.go +++ b/client/firewall/uspfilter/filter_routeacl_test.go @@ -4,7 +4,7 @@ import ( "net/netip" "testing" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/google/gopacket/layers" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/client/grpc/dialer_generic.go b/client/grpc/dialer_generic.go index 479575996..8a80525e9 100644 --- a/client/grpc/dialer_generic.go +++ b/client/grpc/dialer_generic.go @@ -16,28 +16,47 @@ import ( "google.golang.org/grpc" nbnet "github.com/netbirdio/netbird/client/net" + "github.com/netbirdio/netbird/client/netsweep" ) func WithCustomDialer(_ bool, _ string) grpc.DialOption { + return grpc.WithContextDialer(dialContext) +} + +// WithSweeper dials like WithCustomDialer but registers connections and +// dials with the sweeper. Append it after WithCustomDialer: gRPC applies +// dial options in order, so the later context dialer wins. +func WithSweeper(sweeper *netsweep.Sweeper) grpc.DialOption { return grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) { - if runtime.GOOS == "linux" { - currentUser, err := user.Current() - if err != nil { - return nil, status.Errorf(codes.FailedPrecondition, "failed to get current user: %v", err) - } + dial := sweeper.StartDial(ctx) + defer dial.Release() - // the custom dialer requires root permissions which are not required for use cases run as non-root - if currentUser.Uid != "0" { - log.Debug("Not running as root, using standard dialer") - dialer := &net.Dialer{} - return dialer.DialContext(ctx, "tcp", addr) - } - } - - conn, err := nbnet.NewDialer().DialContext(ctx, "tcp", addr) + conn, err := dialContext(dial.Ctx(), addr) if err != nil { - return nil, fmt.Errorf("nbnet.NewDialer().DialContext: %w", err) + return nil, err } - return conn, nil + return dial.WrapConn(conn) }) } + +func dialContext(ctx context.Context, addr string) (net.Conn, error) { + if runtime.GOOS == "linux" { + currentUser, err := user.Current() + if err != nil { + return nil, status.Errorf(codes.FailedPrecondition, "failed to get current user: %v", err) + } + + // the custom dialer requires root permissions which are not required for use cases run as non-root + if currentUser.Uid != "0" { + log.Debug("Not running as root, using standard dialer") + dialer := &net.Dialer{} + return dialer.DialContext(ctx, "tcp", addr) + } + } + + conn, err := nbnet.NewDialer().DialContext(ctx, "tcp", addr) + if err != nil { + return nil, fmt.Errorf("nbnet.NewDialer().DialContext: %w", err) + } + return conn, nil +} diff --git a/client/grpc/dialer_js.go b/client/grpc/dialer_js.go index b89ec3c21..8863756d7 100644 --- a/client/grpc/dialer_js.go +++ b/client/grpc/dialer_js.go @@ -3,6 +3,7 @@ package grpc import ( "google.golang.org/grpc" + "github.com/netbirdio/netbird/client/netsweep" "github.com/netbirdio/netbird/util/wsproxy/client" ) @@ -11,3 +12,8 @@ import ( func WithCustomDialer(tlsEnabled bool, component string) grpc.DialOption { return client.WithWebSocketDialer(tlsEnabled, component) } + +// WithSweeper is a no-op on WASM/JS: there is no network change signal. +func WithSweeper(_ *netsweep.Sweeper) grpc.DialOption { + return grpc.EmptyDialOption{} +} diff --git a/client/grpc/retry.go b/client/grpc/retry.go new file mode 100644 index 000000000..754ffa341 --- /dev/null +++ b/client/grpc/retry.go @@ -0,0 +1,49 @@ +package grpc + +import ( + "context" + "errors" + "time" + + "github.com/cenkalti/backoff/v4" + + "github.com/netbirdio/netbird/client/netstate" +) + +// Retry mirrors backoff.Retry, but the sleep between attempts also wakes on +// OS network availability transitions: an operation cut down by a network +// change retries the moment the network settles instead of sleeping through +// the recovery. A nil netState never fires, leaving plain backoff.Retry +// behavior. +func Retry(ctx context.Context, operation backoff.Operation, bo backoff.BackOff, netState *netstate.State) error { + bo.Reset() + for { + err := operation() + if err == nil { + return nil + } + + var permanent *backoff.PermanentError + if errors.As(err, &permanent) { + return permanent.Err + } + + next := bo.NextBackOff() + if next == backoff.Stop { + if cerr := ctx.Err(); cerr != nil { + return cerr + } + return err + } + + timer := time.NewTimer(next) + select { + case <-timer.C: + case <-netState.Changed(): + timer.Stop() + case <-ctx.Done(): + timer.Stop() + return ctx.Err() + } + } +} diff --git a/client/grpc/retry_test.go b/client/grpc/retry_test.go new file mode 100644 index 000000000..4edca47b6 --- /dev/null +++ b/client/grpc/retry_test.go @@ -0,0 +1,91 @@ +package grpc + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/cenkalti/backoff/v4" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/netbirdio/netbird/client/netstate" +) + +func TestRetryWakesOnNetworkChange(t *testing.T) { + ns := netstate.New() + attempts := 0 + operation := func() error { + attempts++ + if attempts == 1 { + return errors.New("cut by network change") + } + return nil + } + + go func() { + time.Sleep(20 * time.Millisecond) + ns.Set(false) + }() + + start := time.Now() + err := Retry(context.Background(), operation, backoff.NewConstantBackOff(time.Minute), ns) + + require.NoError(t, err) + assert.Equal(t, 2, attempts, "network change must cause one immediate retry") + assert.Less(t, time.Since(start), time.Second, "the transition must cut the minute-long sleep short") +} + +func TestRetryPermanentError(t *testing.T) { + sentinel := errors.New("permission denied") + operation := func() error { + return backoff.Permanent(sentinel) + } + + err := Retry(context.Background(), operation, backoff.NewConstantBackOff(time.Millisecond), nil) + assert.ErrorIs(t, err, sentinel, "permanent errors must stop retries") +} + +func TestRetryNilNetState(t *testing.T) { + attempts := 0 + operation := func() error { + attempts++ + if attempts < 3 { + return errors.New("transient") + } + return nil + } + + err := Retry(context.Background(), operation, backoff.NewConstantBackOff(time.Millisecond), nil) + require.NoError(t, err) + assert.Equal(t, 3, attempts, "nil network state must preserve timed retries") +} + +func TestRetryStops(t *testing.T) { + failure := errors.New("still failing") + operation := func() error { + return failure + } + + err := Retry(context.Background(), operation, &backoff.StopBackOff{}, nil) + assert.ErrorIs(t, err, failure, "stop backoff must return the operation error") +} + +func TestRetryCtxCancelDuringSleep(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + operation := func() error { + return errors.New("failing") + } + + go func() { + time.Sleep(20 * time.Millisecond) + cancel() + }() + + start := time.Now() + err := Retry(ctx, operation, backoff.NewConstantBackOff(time.Minute), netstate.New()) + + assert.ErrorIs(t, err, context.Canceled, "context cancellation must stop the retry loop") + assert.Less(t, time.Since(start), time.Second, "context cancellation must interrupt backoff sleep") +} diff --git a/client/iface/device/device_filter_test.go b/client/iface/device/device_filter_test.go index 0d86c9323..a75ef90f9 100644 --- a/client/iface/device/device_filter_test.go +++ b/client/iface/device/device_filter_test.go @@ -4,7 +4,7 @@ import ( "net" "testing" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/google/gopacket" "github.com/google/gopacket/layers" diff --git a/client/iface/mocks/filter.go b/client/iface/mocks/filter.go index 5ae98039c..ff3dd0c8a 100644 --- a/client/iface/mocks/filter.go +++ b/client/iface/mocks/filter.go @@ -8,7 +8,7 @@ import ( "net/netip" reflect "reflect" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockPacketFilter is a mock of PacketFilter interface. diff --git a/client/iface/mocks/tun.go b/client/iface/mocks/tun.go index 677c82b0b..519ee6005 100644 --- a/client/iface/mocks/tun.go +++ b/client/iface/mocks/tun.go @@ -8,7 +8,7 @@ import ( os "os" reflect "reflect" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" tun "golang.zx2c4.com/wireguard/tun" ) diff --git a/client/iface/wgproxy/bind/proxy.go b/client/iface/wgproxy/bind/proxy.go index be690ed4f..fcaee15c7 100644 --- a/client/iface/wgproxy/bind/proxy.go +++ b/client/iface/wgproxy/bind/proxy.go @@ -53,15 +53,15 @@ func NewProxyBind(bind Bind, mtu uint16) *ProxyBind { return p } -// AddTurnConn adds a new connection to the bind. +// AddRelayedConn adds a new connection to the bind. // endpoint is the NetBird address of the remote peer. The SetEndpoint return with the address what will be used in the // WireGuard configuration. // // Parameters: // - ctx: Context is used for proxyToLocal to avoid unnecessary error messages // - nbAddr: The NetBird UDP address of the remote peer, it required to generate fake address -// - remoteConn: The established TURN connection to the remote peer -func (p *ProxyBind) AddTurnConn(ctx context.Context, nbAddr *net.UDPAddr, remoteConn net.Conn) error { +// - remoteConn: The established relayed connection to the remote peer +func (p *ProxyBind) AddRelayedConn(ctx context.Context, nbAddr *net.UDPAddr, remoteConn net.Conn) error { fakeNetIP, err := fakeAddress(nbAddr) if err != nil { return err diff --git a/client/iface/wgproxy/ebpf/proxy.go b/client/iface/wgproxy/ebpf/proxy.go index 1b1a8ce1c..91c741c0d 100644 --- a/client/iface/wgproxy/ebpf/proxy.go +++ b/client/iface/wgproxy/ebpf/proxy.go @@ -30,9 +30,9 @@ type WGEBPFProxy struct { proxyPort int mtu uint16 - ebpfManager ebpfMgr.Manager - turnConnStore map[uint16]net.Conn - turnConnMutex sync.Mutex + ebpfManager ebpfMgr.Manager + relayedConnStore map[uint16]net.Conn + relayedConnMutex sync.Mutex lastUsedPort uint16 rawConnIPv4 net.PacketConn @@ -50,7 +50,7 @@ func NewWGEBPFProxy(wgPort int, mtu uint16) *WGEBPFProxy { localWGListenPort: wgPort, mtu: mtu, ebpfManager: ebpf.GetEbpfManagerInstance(), - turnConnStore: make(map[uint16]net.Conn), + relayedConnStore: make(map[uint16]net.Conn), } return wgProxy } @@ -110,14 +110,14 @@ func (p *WGEBPFProxy) Listen() error { return nil } -// AddTurnConn add new turn connection for the proxy -func (p *WGEBPFProxy) AddTurnConn(turnConn net.Conn) (*net.UDPAddr, error) { - wgEndpointPort, err := p.storeTurnConn(turnConn) +// AddRelayedConn add new relayed connection for the proxy +func (p *WGEBPFProxy) AddRelayedConn(relayedConn net.Conn) (*net.UDPAddr, error) { + wgEndpointPort, err := p.storeRelayedConn(relayedConn) if err != nil { return nil, err } - log.Infof("turn conn added to wg proxy store: %s, endpoint port: :%d", turnConn.RemoteAddr(), wgEndpointPort) + log.Infof("relayed conn added to wg proxy store: %s, endpoint port: :%d", relayedConn.RemoteAddr(), wgEndpointPort) wgEndpoint := &net.UDPAddr{ IP: net.ParseIP(loopbackAddr), @@ -186,48 +186,48 @@ func (p *WGEBPFProxy) readAndForwardPacket(buf []byte) error { return fmt.Errorf("failed to read UDP packet from WG: %w", err) } - p.turnConnMutex.Lock() - conn, ok := p.turnConnStore[uint16(addr.Port)] - p.turnConnMutex.Unlock() + p.relayedConnMutex.Lock() + conn, ok := p.relayedConnStore[uint16(addr.Port)] + p.relayedConnMutex.Unlock() if !ok { if p.ctx.Err() == nil { - log.Debugf("turn conn not found by port because conn already has been closed: %d", addr.Port) + log.Debugf("relayed conn not found by port because conn already has been closed: %d", addr.Port) } return nil } if _, err := conn.Write(buf[:n]); err != nil { - return fmt.Errorf("failed to forward local WG packet (%d) to remote turn conn: %w", addr.Port, err) + return fmt.Errorf("forward local WG packet (%d) to remote relayed conn: %w", addr.Port, err) } return nil } -func (p *WGEBPFProxy) storeTurnConn(turnConn net.Conn) (uint16, error) { - p.turnConnMutex.Lock() - defer p.turnConnMutex.Unlock() +func (p *WGEBPFProxy) storeRelayedConn(relayedConn net.Conn) (uint16, error) { + p.relayedConnMutex.Lock() + defer p.relayedConnMutex.Unlock() np, err := p.nextFreePort() if err != nil { return np, err } - p.turnConnStore[np] = turnConn + p.relayedConnStore[np] = relayedConn return np, nil } -func (p *WGEBPFProxy) removeTurnConn(turnConnID uint16) { - p.turnConnMutex.Lock() - defer p.turnConnMutex.Unlock() +func (p *WGEBPFProxy) removeRelayedConn(relayedConnID uint16) { + p.relayedConnMutex.Lock() + defer p.relayedConnMutex.Unlock() - _, ok := p.turnConnStore[turnConnID] + _, ok := p.relayedConnStore[relayedConnID] if ok { - log.Debugf("remove turn conn from store by port: %d", turnConnID) + log.Debugf("remove relayed conn from store by port: %d", relayedConnID) } - delete(p.turnConnStore, turnConnID) + delete(p.relayedConnStore, relayedConnID) } func (p *WGEBPFProxy) nextFreePort() (uint16, error) { - if len(p.turnConnStore) == 65535 { - return 0, fmt.Errorf("reached maximum turn connection numbers") + if len(p.relayedConnStore) == 65535 { + return 0, fmt.Errorf("reached maximum relayed connection numbers") } generatePort: if p.lastUsedPort == 65535 { @@ -236,7 +236,7 @@ generatePort: p.lastUsedPort++ } - if _, ok := p.turnConnStore[p.lastUsedPort]; ok { + if _, ok := p.relayedConnStore[p.lastUsedPort]; ok { goto generatePort } return p.lastUsedPort, nil diff --git a/client/iface/wgproxy/ebpf/proxy_test.go b/client/iface/wgproxy/ebpf/proxy_test.go index 3ec4f0eba..228c06c9b 100644 --- a/client/iface/wgproxy/ebpf/proxy_test.go +++ b/client/iface/wgproxy/ebpf/proxy_test.go @@ -9,32 +9,32 @@ import ( func TestWGEBPFProxy_connStore(t *testing.T) { wgProxy := NewWGEBPFProxy(1, 1280) - p, _ := wgProxy.storeTurnConn(nil) + p, _ := wgProxy.storeRelayedConn(nil) if p != 1 { t.Errorf("invalid initial port: %d", wgProxy.lastUsedPort) } numOfConns := 10 for i := 0; i < numOfConns; i++ { - p, _ = wgProxy.storeTurnConn(nil) + p, _ = wgProxy.storeRelayedConn(nil) } if p != uint16(numOfConns)+1 { t.Errorf("invalid last used port: %d, expected: %d", p, numOfConns+1) } - if len(wgProxy.turnConnStore) != numOfConns+1 { - t.Errorf("invalid store size: %d, expected: %d", len(wgProxy.turnConnStore), numOfConns+1) + if len(wgProxy.relayedConnStore) != numOfConns+1 { + t.Errorf("invalid store size: %d, expected: %d", len(wgProxy.relayedConnStore), numOfConns+1) } } func TestWGEBPFProxy_portCalculation_overflow(t *testing.T) { wgProxy := NewWGEBPFProxy(1, 1280) - _, _ = wgProxy.storeTurnConn(nil) + _, _ = wgProxy.storeRelayedConn(nil) wgProxy.lastUsedPort = 65535 - p, _ := wgProxy.storeTurnConn(nil) + p, _ := wgProxy.storeRelayedConn(nil) - if len(wgProxy.turnConnStore) != 2 { - t.Errorf("invalid store size: %d, expected: %d", len(wgProxy.turnConnStore), 2) + if len(wgProxy.relayedConnStore) != 2 { + t.Errorf("invalid store size: %d, expected: %d", len(wgProxy.relayedConnStore), 2) } if p != 2 { @@ -46,11 +46,11 @@ func TestWGEBPFProxy_portCalculation_maxConn(t *testing.T) { wgProxy := NewWGEBPFProxy(1, 1280) for i := 0; i < 65535; i++ { - _, _ = wgProxy.storeTurnConn(nil) + _, _ = wgProxy.storeRelayedConn(nil) } - _, err := wgProxy.storeTurnConn(nil) + _, err := wgProxy.storeRelayedConn(nil) if err == nil { - t.Errorf("invalid turn conn store calculation") + t.Errorf("invalid relayed conn store calculation") } } diff --git a/client/iface/wgproxy/ebpf/wrapper.go b/client/iface/wgproxy/ebpf/wrapper.go index a6156a661..f75e21aa6 100644 --- a/client/iface/wgproxy/ebpf/wrapper.go +++ b/client/iface/wgproxy/ebpf/wrapper.go @@ -121,10 +121,10 @@ func NewProxyWrapper(proxy *WGEBPFProxy) *ProxyWrapper { } } -func (p *ProxyWrapper) AddTurnConn(ctx context.Context, _ *net.UDPAddr, remoteConn net.Conn) error { - addr, err := p.wgeBPFProxy.AddTurnConn(remoteConn) +func (p *ProxyWrapper) AddRelayedConn(ctx context.Context, _ *net.UDPAddr, remoteConn net.Conn) error { + addr, err := p.wgeBPFProxy.AddRelayedConn(remoteConn) if err != nil { - return fmt.Errorf("add turn conn: %w", err) + return fmt.Errorf("add relayed conn: %w", err) } headers, err := NewPacketHeaders(p.wgeBPFProxy.localWGListenPort, addr) @@ -252,7 +252,7 @@ func (p *ProxyWrapper) CloseConn() error { } func (p *ProxyWrapper) proxyToLocal(ctx context.Context) { - defer p.wgeBPFProxy.removeTurnConn(uint16(p.wgRelayedEndpointAddr.Port)) + defer p.wgeBPFProxy.removeRelayedConn(uint16(p.wgRelayedEndpointAddr.Port)) buf := make([]byte, p.wgeBPFProxy.mtu+bufsize.WGBufferOverhead) for { @@ -273,7 +273,7 @@ func (p *ProxyWrapper) proxyToLocal(ctx context.Context) { if ctx.Err() != nil { return } - log.Errorf("failed to write out turn pkg to local conn: %v", err) + log.Errorf("failed to write out relayed pkg to local conn: %v", err) } } } @@ -286,7 +286,7 @@ func (p *ProxyWrapper) readFromRemote(ctx context.Context, buf []byte) (int, err } p.closeListener.Notify() if !errors.Is(err, io.EOF) { - log.Errorf("failed to read from turn conn (endpoint: :%d): %s", p.wgRelayedEndpointAddr.Port, err) + log.Errorf("failed to read from relayed conn (endpoint: :%d): %s", p.wgRelayedEndpointAddr.Port, err) } return 0, err } diff --git a/client/iface/wgproxy/proxy.go b/client/iface/wgproxy/proxy.go index 40346bc15..b0033bffa 100644 --- a/client/iface/wgproxy/proxy.go +++ b/client/iface/wgproxy/proxy.go @@ -7,7 +7,7 @@ import ( // Proxy is a transfer layer between the relayed connection and the WireGuard type Proxy interface { - AddTurnConn(ctx context.Context, endpoint *net.UDPAddr, remoteConn net.Conn) error + AddRelayedConn(ctx context.Context, endpoint *net.UDPAddr, remoteConn net.Conn) error EndpointAddr() *net.UDPAddr // EndpointAddr returns the address of the WireGuard peer endpoint Work() // Work start or resume the proxy Pause() // Pause to forward the packages from remote connection to WireGuard. The opposite way still works. diff --git a/client/iface/wgproxy/proxy_test.go b/client/iface/wgproxy/proxy_test.go index 1aeab66b7..d86cdbe80 100644 --- a/client/iface/wgproxy/proxy_test.go +++ b/client/iface/wgproxy/proxy_test.go @@ -95,7 +95,7 @@ func TestProxyCloseByRemoteConn(t *testing.T) { t.Run(tt.name, func(t *testing.T) { addr, _ := net.ResolveUDPAddr("udp", "100.108.135.221:51892") relayedConn := newMockConn() - err := tt.proxy.AddTurnConn(ctx, addr, relayedConn) + err := tt.proxy.AddRelayedConn(ctx, addr, relayedConn) if err != nil { t.Errorf("error: %v", err) } @@ -157,7 +157,7 @@ func redirectTraffic(t *testing.T, proxy Proxy, wgPort int, endPointAddr *net.UD _ = relayedServer.Close() }() - if err := proxy.AddTurnConn(context.Background(), endPointAddr, relayedConn); err != nil { + if err := proxy.AddRelayedConn(context.Background(), endPointAddr, relayedConn); err != nil { t.Errorf("error: %v", err) } defer func() { diff --git a/client/iface/wgproxy/redirect_test.go b/client/iface/wgproxy/redirect_test.go index 135970838..f0d59cc64 100644 --- a/client/iface/wgproxy/redirect_test.go +++ b/client/iface/wgproxy/redirect_test.go @@ -119,9 +119,9 @@ func testRedirectAs(t *testing.T, proxy Proxy, wgPort int, nbAddr, p2pEndpoint * } defer relayConn.Close() - // Add TURN connection to proxy - if err := proxy.AddTurnConn(ctx, nbAddr, relayConn); err != nil { - t.Fatalf("failed to add TURN connection: %v", err) + // Add relayed connection to proxy + if err := proxy.AddRelayedConn(ctx, nbAddr, relayConn); err != nil { + t.Fatalf("failed to add relayed connection: %v", err) } defer func() { if err := proxy.CloseConn(); err != nil { @@ -304,8 +304,8 @@ func TestRedirectAs_Multiple_Switches(t *testing.T) { Port: 38746, } - if err := proxy.AddTurnConn(ctx, nbAddr, relayConn); err != nil { - t.Fatalf("failed to add TURN connection: %v", err) + if err := proxy.AddRelayedConn(ctx, nbAddr, relayConn); err != nil { + t.Fatalf("failed to add relayed connection: %v", err) } defer func() { if err := proxy.CloseConn(); err != nil { diff --git a/client/iface/wgproxy/udp/proxy.go b/client/iface/wgproxy/udp/proxy.go index 783843aba..a0895c8c7 100644 --- a/client/iface/wgproxy/udp/proxy.go +++ b/client/iface/wgproxy/udp/proxy.go @@ -51,12 +51,12 @@ func NewWGUDPProxy(wgPort int, mtu uint16) *WGUDPProxy { return p } -// AddTurnConn +// AddRelayedConn dials the local WireGuard port and stores the relayed connection. // The provided Context must be non-nil. If the context expires before // the connection is complete, an error is returned. Once successfully // connected, any expiration of the context will not affect the // connection. -func (p *WGUDPProxy) AddTurnConn(ctx context.Context, _ *net.UDPAddr, remoteConn net.Conn) error { +func (p *WGUDPProxy) AddRelayedConn(ctx context.Context, _ *net.UDPAddr, remoteConn net.Conn) error { dialer := net.Dialer{} localConn, err := dialer.DialContext(ctx, "udp", fmt.Sprintf(":%d", p.localWGListenPort)) if err != nil { diff --git a/client/internal/acl/manager.go b/client/internal/acl/manager.go index d9b179457..cbd9c5ab1 100644 --- a/client/internal/acl/manager.go +++ b/client/internal/acl/manager.go @@ -116,11 +116,11 @@ func (d *DefaultManager) ApplyFiltering(networkMap *mgmProto.NetworkMap, dnsRout // firewall state, so an identical hash means an identical resulting ruleset. func (d *DefaultManager) firewallConfigHash(networkMap *mgmProto.NetworkMap, dnsRouteFeatureFlag bool) (uint64, error) { return hashstructure.Hash(struct { - PeerRules []*mgmProto.FirewallRule - PeerRulesIsEmpty bool - RouteRules []*mgmProto.RouteFirewallRule - RouteRulesIsEmpty bool - DNSRouteFeatureFlag bool + PeerRules []*mgmProto.FirewallRule + PeerRulesIsEmpty bool + RouteRules []*mgmProto.RouteFirewallRule + RouteRulesIsEmpty bool + DNSRouteFeatureFlag bool }{ PeerRules: networkMap.GetFirewallRules(), PeerRulesIsEmpty: networkMap.GetFirewallRulesIsEmpty(), @@ -144,13 +144,13 @@ func (d *DefaultManager) applyPeerACLs(networkMap *mgmProto.NetworkMap) { log.Warn("this peer is connected to a NetBird Management service with an older version. Allowing all traffic from connected peers") rules = append(rules, &mgmProto.FirewallRule{ - PeerIP: "0.0.0.0", + PeerIP: "0.0.0.0", //nolint:staticcheck Direction: mgmProto.RuleDirection_IN, Action: mgmProto.RuleAction_ACCEPT, Protocol: mgmProto.RuleProtocol_ALL, }, &mgmProto.FirewallRule{ - PeerIP: "0.0.0.0", + PeerIP: "0.0.0.0", //nolint:staticcheck Direction: mgmProto.RuleDirection_OUT, Action: mgmProto.RuleAction_ACCEPT, Protocol: mgmProto.RuleProtocol_ALL, @@ -407,7 +407,6 @@ func (d *DefaultManager) getRuleGroupingSelector(rule *mgmProto.FirewallRule) st return fmt.Sprintf("%v:%v:%v:%s:%v", strconv.Itoa(int(rule.Direction)), rule.Action, rule.Protocol, rule.Port, rule.PortInfo) } - // extractRuleIP extracts the peer IP from a firewall rule. // If sourcePrefixes is populated (new management), decode the first entry and use its address. // Otherwise fall back to the deprecated PeerIP string field (old management). diff --git a/client/internal/acl/manager_test.go b/client/internal/acl/manager_test.go index 968654ae9..8f737706e 100644 --- a/client/internal/acl/manager_test.go +++ b/client/internal/acl/manager_test.go @@ -5,9 +5,9 @@ import ( "net/netip" "testing" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" "github.com/netbirdio/netbird/client/firewall" "github.com/netbirdio/netbird/client/iface" @@ -87,7 +87,7 @@ func TestDefaultManager(t *testing.T) { networkMap.FirewallRules = append( networkMap.FirewallRules, &mgmProto.FirewallRule{ - PeerIP: "10.93.0.3", + PeerIP: "10.93.0.3", //nolint:staticcheck Direction: mgmProto.RuleDirection_IN, Action: mgmProto.RuleAction_DROP, Protocol: mgmProto.RuleProtocol_ICMP, @@ -556,12 +556,12 @@ func TestApplyFilteringSkipsUnchangedConfig(t *testing.T) { func buildNetworkMap(peerRules, routeRules int) *mgmProto.NetworkMap { nm := &mgmProto.NetworkMap{ - FirewallRulesIsEmpty: peerRules == 0, + FirewallRulesIsEmpty: peerRules == 0, RoutesFirewallRulesIsEmpty: routeRules == 0, } for i := range peerRules { nm.FirewallRules = append(nm.FirewallRules, &mgmProto.FirewallRule{ - PeerIP: fmt.Sprintf("10.%d.%d.%d", i>>16&0xff, i>>8&0xff, i&0xff), + PeerIP: fmt.Sprintf("10.%d.%d.%d", i>>16&0xff, i>>8&0xff, i&0xff), //nolint:staticcheck Direction: mgmProto.RuleDirection_IN, Action: mgmProto.RuleAction_ACCEPT, Protocol: mgmProto.RuleProtocol_TCP, diff --git a/client/internal/acl/mocks/iface_mapper.go b/client/internal/acl/mocks/iface_mapper.go index 95d5a2c58..f8cca1c2d 100644 --- a/client/internal/acl/mocks/iface_mapper.go +++ b/client/internal/acl/mocks/iface_mapper.go @@ -7,7 +7,7 @@ package mocks import ( reflect "reflect" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" wgdevice "golang.zx2c4.com/wireguard/device" "github.com/netbirdio/netbird/client/iface/device" diff --git a/client/internal/auth/auth.go b/client/internal/auth/auth.go index 153727a6c..b3a9e1158 100644 --- a/client/internal/auth/auth.go +++ b/client/internal/auth/auth.go @@ -138,26 +138,37 @@ func (a *Auth) IsSSOSupported(ctx context.Context) (bool, error) { // GetOAuthFlow returns an OAuth flow (PKCE or Device) using the existing management connection // This avoids creating a new connection to the management server -func (a *Auth) GetOAuthFlow(ctx context.Context, forceDeviceAuth bool) (OAuthFlow, error) { +func (a *Auth) GetOAuthFlow(ctx context.Context, forceDeviceAuth bool, hint string) (OAuthFlow, error) { var flow OAuthFlow - var err error - err = a.withRetry(ctx, func(client *mgm.GrpcClient) error { + err := a.withRetry(ctx, func(client *mgm.GrpcClient) error { if forceDeviceAuth { - flow, err = a.getDeviceFlow(client) - return err + deviceFlow, err := a.getDeviceFlow(client) + if err != nil { + return err + } + deviceFlow.SetLoginHint(hint) + flow = deviceFlow + return nil } // Try PKCE flow first - flow, err = a.getPKCEFlow(client) + pkceFlow, err := a.getPKCEFlow(client) if err != nil { // If PKCE not supported, try Device flow if s, ok := status.FromError(err); ok && (s.Code() == codes.NotFound || s.Code() == codes.Unimplemented) { - flow, err = a.getDeviceFlow(client) - return err + deviceFlow, err := a.getDeviceFlow(client) + if err != nil { + return err + } + deviceFlow.SetLoginHint(hint) + flow = deviceFlow + return nil } return err } + pkceFlow.SetLoginHint(hint) + flow = pkceFlow return nil }) diff --git a/client/internal/auth/oauth.go b/client/internal/auth/oauth.go index a50a2ce6f..91329c98b 100644 --- a/client/internal/auth/oauth.go +++ b/client/internal/auth/oauth.go @@ -97,9 +97,7 @@ func authenticateWithPKCEFlow(ctx context.Context, config *profilemanager.Config return nil, fmt.Errorf("getting pkce authorization flow info failed with error: %v", err) } - if hint != "" { - pkceFlowInfo.SetLoginHint(hint) - } + pkceFlowInfo.SetLoginHint(hint) return pkceFlowInfo, nil } @@ -127,9 +125,7 @@ func authenticateWithDeviceCodeFlow(ctx context.Context, config *profilemanager. } } - if hint != "" { - deviceFlowInfo.SetLoginHint(hint) - } + deviceFlowInfo.SetLoginHint(hint) return deviceFlowInfo, nil } diff --git a/client/internal/connect.go b/client/internal/connect.go index ceb39419e..e45ecca44 100644 --- a/client/internal/connect.go +++ b/client/internal/connect.go @@ -38,6 +38,8 @@ import ( "github.com/netbirdio/netbird/client/internal/updater" "github.com/netbirdio/netbird/client/internal/updater/installer" nbnet "github.com/netbirdio/netbird/client/net" + "github.com/netbirdio/netbird/client/netstate" + "github.com/netbirdio/netbird/client/netsweep" cProto "github.com/netbirdio/netbird/client/proto" "github.com/netbirdio/netbird/client/ssh" sshconfig "github.com/netbirdio/netbird/client/ssh/config" @@ -70,18 +72,42 @@ type ConnectClient struct { updateManager *updater.Manager persistSyncResponse bool + + // netState gates every reconnection loop on OS-reported network + // availability. Nil (the default) disables gating; mobile platforms + // inject it via WithNetworkState. + netState *netstate.State + + // sweeper cuts the management, signal and relay connections on network + // change; nil disables it. + sweeper *netsweep.Sweeper +} + +// ConnectClientOption configures optional ConnectClient behavior. +type ConnectClientOption func(*ConnectClient) + +// WithNetworkState injects the OS network availability state that gates every +// reconnection loop; without it gating is disabled. +func WithNetworkState(netState *netstate.State) ConnectClientOption { + return func(c *ConnectClient) { c.netState = netState } +} + +// WithSweeper injects the network change sweeper. +func WithSweeper(sweeper *netsweep.Sweeper) ConnectClientOption { + return func(c *ConnectClient) { c.sweeper = sweeper } } func NewConnectClient( ctx context.Context, config *profilemanager.Config, statusRecorder *peer.Status, + opts ...ConnectClientOption, ) *ConnectClient { // Derive the run context here so Stop owns the cancel that unblocks the run // loop. runCancel is set once at construction, so Stop can call it without // racing the run loop's startup. Callers therefore need not cancel before Stop. runCtx, runCancel := context.WithCancel(ctx) - return &ConnectClient{ + c := &ConnectClient{ ctx: runCtx, runCancel: runCancel, runExited: make(chan struct{}), @@ -89,6 +115,10 @@ func NewConnectClient( statusRecorder: statusRecorder, engineMutex: sync.Mutex{}, } + for _, opt := range opts { + opt(c) + } + return c } func (c *ConnectClient) SetUpdateManager(um *updater.Manager) { @@ -274,6 +304,13 @@ func (c *ConnectClient) run(mobileDependency MobileDependency, runningChan chan return nil } + // suspend connection attempts while the OS reports no usable network + if waited, err := c.netState.Wait(c.ctx); err != nil { + return nil + } else if waited { + backOff.Reset() + } + state.Set(StatusConnecting) engineCtx, cancel := context.WithCancel(c.ctx) @@ -285,7 +322,8 @@ func (c *ConnectClient) run(mobileDependency MobileDependency, runningChan chan }() log.Debugf("connecting to the Management service %s", c.config.ManagementURL.Host) - mgmClient, err := mgm.NewClient(engineCtx, c.config.ManagementURL.Host, myPrivateKey, mgmTlsEnabled) + mgmClient, err := mgm.NewClient(engineCtx, c.config.ManagementURL.Host, myPrivateKey, mgmTlsEnabled, + mgm.WithNetworkState(c.netState), mgm.WithSweeper(c.sweeper)) if err != nil { // On daemon shutdown / Down() the parent context is cancelled // and the dial fails with "context canceled". Wrapping that @@ -360,7 +398,7 @@ func (c *ConnectClient) run(mobileDependency MobileDependency, runningChan chan }() // with the global Netbird config in hand connect (just a connection, no stream yet) Signal - signalClient, err := connectToSignal(engineCtx, loginResp.GetNetbirdConfig(), myPrivateKey) + signalClient, err := connectToSignal(engineCtx, loginResp.GetNetbirdConfig(), myPrivateKey, c.netState, c.sweeper) if err != nil { log.Error(err) return wrapErr(err) @@ -396,7 +434,8 @@ func (c *ConnectClient) run(mobileDependency MobileDependency, runningChan chan engineConfig.StateDir = filepath.Dir(path) } - relayManager := relayClient.NewManager(engineCtx, relayURLs, myPrivateKey.PublicKey().String(), engineConfig.MTU) + relayManager := relayClient.NewManager(engineCtx, relayURLs, myPrivateKey.PublicKey().String(), engineConfig.MTU, + relayClient.WithNetworkState(c.netState), relayClient.WithSweeper(c.sweeper)) c.statusRecorder.SetRelayMgr(relayManager) if len(relayURLs) > 0 { if token != nil { @@ -424,6 +463,7 @@ func (c *ConnectClient) run(mobileDependency MobileDependency, runningChan chan UpdateManager: c.updateManager, ClientMetrics: c.clientMetrics, MetricsCtx: c.ctx, + NetState: c.netState, }, mobileDependency) engine.SetSyncResponsePersistence(c.persistSyncResponse) c.engine = engine @@ -480,6 +520,16 @@ func (c *ConnectClient) run(mobileDependency MobileDependency, runningChan chan // status stream stuck at Connecting. err = backoff.Retry(operation, backoff.WithContext(backOff, c.ctx)) if err != nil { + // Once the client context is cancelled backoff.WithContext surfaces the + // bare context error, and any attempt torn down mid-flight reports the + // same. That cancellation is the caller asking us to stop (Stop, Down or + // an engine restart), so exit cleanly instead of handing back a failure + // the caller would have to distinguish from a real one. + if c.ctx.Err() != nil && errors.Is(err, context.Canceled) { + log.Info("exiting client retry loop, context cancelled") + return nil + } + log.Debugf("exiting client retry loop due to unrecoverable error: %s", err) if s, ok := gstatus.FromError(err); ok && (s.Code() == codes.PermissionDenied) { state.Set(StatusNeedsLogin) @@ -673,7 +723,7 @@ func selectMTU(localMTU uint16, peerMTU int32) uint16 { } // connectToSignal creates Signal Service client and established a connection -func connectToSignal(ctx context.Context, wtConfig *mgmProto.NetbirdConfig, ourPrivateKey wgtypes.Key) (*signal.GrpcClient, error) { +func connectToSignal(ctx context.Context, wtConfig *mgmProto.NetbirdConfig, ourPrivateKey wgtypes.Key, netState *netstate.State, sweeper *netsweep.Sweeper) (*signal.GrpcClient, error) { var sigTLSEnabled bool if wtConfig.Signal.Protocol == mgmProto.HostConfig_HTTPS { sigTLSEnabled = true @@ -681,7 +731,8 @@ func connectToSignal(ctx context.Context, wtConfig *mgmProto.NetbirdConfig, ourP sigTLSEnabled = false } - signalClient, err := signal.NewClient(ctx, wtConfig.Signal.Uri, ourPrivateKey, sigTLSEnabled) + signalClient, err := signal.NewClient(ctx, wtConfig.Signal.Uri, ourPrivateKey, sigTLSEnabled, + signal.WithNetworkState(netState), signal.WithSweeper(sweeper)) if err != nil { log.Errorf("error while connecting to the Signal Exchange Service %s: %s", wtConfig.Signal.Uri, err) return nil, gstatus.Errorf(codes.FailedPrecondition, "failed connecting to Signal Service : %s", err) diff --git a/client/internal/dns/host_windows.go b/client/internal/dns/host_windows.go index d20fdd1d6..53380b2aa 100644 --- a/client/internal/dns/host_windows.go +++ b/client/internal/dns/host_windows.go @@ -35,6 +35,8 @@ var ( // exported so a diagnostic reader reports the same locations that are written. const ( // NRPTKeyPrefix starts the name of every NRPT rule key this client creates. + // Older versions used different layouts under the same prefix: a single + // unsuffixed key, then one key per domain, now one key per batch of domains. NRPTKeyPrefix = "NetBird-Match" // DNSPolicyConfigRoot holds the NRPT rules of the local policy store. @@ -89,7 +91,6 @@ type registryConfigurator struct { guid string routingAll bool gpo bool - nrptEntryCount int origNameservers []netip.Addr } @@ -322,14 +323,9 @@ func (r *registryConfigurator) applyDNSConfig(config HostDNSConfig, stateManager } if len(matchDomains) != 0 { - count, err := r.addDNSMatchPolicy(matchDomains, config.ServerIP) - // Update count even on error to ensure cleanup covers partially created rules - r.nrptEntryCount = count - if err != nil { + if err := r.addDNSMatchPolicy(matchDomains, config.ServerIP); err != nil { return fmt.Errorf("add dns match policy: %w", err) } - } else { - r.nrptEntryCount = 0 } r.updateState(stateManager) @@ -345,9 +341,8 @@ func (r *registryConfigurator) applyDNSConfig(config HostDNSConfig, stateManager func (r *registryConfigurator) updateState(stateManager *statemanager.Manager) { if err := stateManager.UpdateState(&ShutdownState{ - Guid: r.guid, - GPO: r.gpo, - NRPTEntryCount: r.nrptEntryCount, + Guid: r.guid, + GPO: r.gpo, }); err != nil { log.Errorf("failed to update shutdown state: %s", err) } @@ -362,7 +357,7 @@ func (r *registryConfigurator) addDNSSetupForAll(ip netip.Addr) error { return nil } -func (r *registryConfigurator) addDNSMatchPolicy(domains []string, ip netip.Addr) (int, error) { +func (r *registryConfigurator) addDNSMatchPolicy(domains []string, ip netip.Addr) error { // if the gpo key is present, we need to put our DNS settings there, otherwise our config might be ignored // see https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gpnrpt/8cc31cb9-20cb-4140-9e85-3e08703b4745 @@ -379,19 +374,17 @@ func (r *registryConfigurator) addDNSMatchPolicy(domains []string, ip netip.Addr gpoPath := fmt.Sprintf("%s-%d", gpoDnsPolicyConfigMatchPath, ruleIndex) if err := r.configureDNSPolicy(localPath, batchDomains, ip); err != nil { - return ruleIndex, fmt.Errorf("configure DNS Local policy for rule %d: %w", ruleIndex, err) + return fmt.Errorf("configure DNS Local policy for rule %d: %w", ruleIndex, err) } - // Increment immediately so the caller's cleanup path knows about this rule - ruleIndex++ - if r.gpo { if err := r.configureDNSPolicy(gpoPath, batchDomains, ip); err != nil { - return ruleIndex, fmt.Errorf("configure gpo DNS policy for rule %d: %w", ruleIndex-1, err) + return fmt.Errorf("configure gpo DNS policy for rule %d: %w", ruleIndex, err) } } - log.Debugf("added NRPT rule %d with %d domains", ruleIndex-1, len(batchDomains)) + log.Debugf("added NRPT rule %d with %d domains", ruleIndex, len(batchDomains)) + ruleIndex++ } if r.gpo { @@ -401,7 +394,7 @@ func (r *registryConfigurator) addDNSMatchPolicy(domains []string, ip netip.Addr } log.Infof("added %d NRPT rules for %d domains", ruleIndex, len(domains)) - return ruleIndex, nil + return nil } func (r *registryConfigurator) configureDNSPolicy(policyPath string, domains []string, ip netip.Addr) error { @@ -466,7 +459,7 @@ func (r *registryConfigurator) flushDNSCache() { ret, _, err := dnsFlushResolverCacheFn.Call() if ret == 0 { - if err != nil && !errors.Is(err, syscall.Errno(0)) { + if !errors.Is(err, syscall.Errno(0)) { log.Errorf("DnsFlushResolverCache failed: %v", err) return } @@ -534,28 +527,28 @@ func (r *registryConfigurator) restoreHostDNS() error { return nil } +// removeDNSMatchPolicies deletes every NRPT rule this client may have created, +// from the local and the GPO policy store. The rules are found by enumerating +// the registry, the only authoritative record of what was written. Cleanup must +// not depend on a rule count: the in-memory one is scoped to a single +// registryConfigurator and the persisted one is deleted on every clean +// disconnect, and a rule left behind keeps resolving names over an interface +// that is gone, until reboot discards the volatile key. func (r *registryConfigurator) removeDNSMatchPolicies() error { var merr *multierror.Error - // Try to remove the base entries (for backward compatibility) - if err := removeRegistryKeyFromDNSPolicyConfig(dnsPolicyConfigMatchPath); err != nil { - merr = multierror.Append(merr, fmt.Errorf("remove local base entry: %w", err)) - } - - if err := removeRegistryKeyFromDNSPolicyConfig(gpoDnsPolicyConfigMatchPath); err != nil { - merr = multierror.Append(merr, fmt.Errorf("remove GPO base entry: %w", err)) - } - - for i := 0; i < r.nrptEntryCount; i++ { - localPath := fmt.Sprintf("%s-%d", dnsPolicyConfigMatchPath, i) - gpoPath := fmt.Sprintf("%s-%d", gpoDnsPolicyConfigMatchPath, i) - - if err := removeRegistryKeyFromDNSPolicyConfig(localPath); err != nil { - merr = multierror.Append(merr, fmt.Errorf("remove local entry %d: %w", i, err)) + for _, root := range []string{DNSPolicyConfigRoot, GPODNSPolicyConfigRoot} { + names, err := listNRPTRuleKeys(root) + if err != nil { + merr = multierror.Append(merr, fmt.Errorf("list rule keys under %s: %w", root, err)) + continue } - if err := removeRegistryKeyFromDNSPolicyConfig(gpoPath); err != nil { - merr = multierror.Append(merr, fmt.Errorf("remove GPO entry %d: %w", i, err)) + for _, name := range names { + path := root + `\` + name + if err := removeRegistryKeyFromDNSPolicyConfig(path); err != nil { + merr = multierror.Append(merr, fmt.Errorf("remove entry %s: %w", path, err)) + } } } @@ -570,6 +563,39 @@ func (r *registryConfigurator) restoreUncleanShutdownDNS() error { return r.restoreHostDNS() } +// listNRPTRuleKeys returns the names of our NRPT rule keys under a policy store +// root. An absent root holds nothing to clean up, which is the normal state of +// the GPO store on a machine without DNS Client policy. +func listNRPTRuleKeys(root string) ([]string, error) { + k, err := registry.OpenKey(registry.LOCAL_MACHINE, root, registry.ENUMERATE_SUB_KEYS) + switch { + case errors.Is(err, registry.ErrNotExist), errors.Is(err, syscall.ERROR_PATH_NOT_FOUND): + // the GPO store is absent on a machine without DNS client policy + log.Debugf("HKEY_LOCAL_MACHINE\\%s does not exist", root) + return nil, nil + case err != nil: + // any other failure has to reach the caller: reporting no rules would + // report a successful cleanup while leaving the rules in place + return nil, fmt.Errorf("open HKEY_LOCAL_MACHINE\\%s: %w", root, err) + } + defer closer(k) + + names, err := k.ReadSubKeyNames(-1) + if err != nil { + return nil, fmt.Errorf("read subkey names: %w", err) + } + + var ruleKeys []string + for _, name := range names { + // registry key names are case insensitive + if strings.HasPrefix(strings.ToLower(name), strings.ToLower(NRPTKeyPrefix)) { + ruleKeys = append(ruleKeys, name) + } + } + + return ruleKeys, nil +} + func removeRegistryKeyFromDNSPolicyConfig(regKeyPath string) error { k, err := registry.OpenKey(registry.LOCAL_MACHINE, regKeyPath, registry.QUERY_VALUE) if err != nil { @@ -601,7 +627,7 @@ func refreshGroupPolicy() error { ) if ret == 0 { - if err != nil && !errors.Is(err, syscall.Errno(0)) { + if !errors.Is(err, syscall.Errno(0)) { return fmt.Errorf("RefreshPolicyEx failed: %w", err) } return fmt.Errorf("RefreshPolicyEx failed") diff --git a/client/internal/dns/host_windows_test.go b/client/internal/dns/host_windows_test.go index 3cd2b1bd5..861613c95 100644 --- a/client/internal/dns/host_windows_test.go +++ b/client/internal/dns/host_windows_test.go @@ -25,7 +25,7 @@ func TestNRPTEntriesCleanupOnConfigChange(t *testing.T) { // Create a test interface registry key so updateSearchDomains doesn't fail testGUID := "{12345678-1234-1234-1234-123456789ABC}" - interfacePath := `SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\` + testGUID + interfacePath := InterfaceConfigPath + `\` + testGUID testKey, _, err := registry.CreateKey(registry.LOCAL_MACHINE, interfacePath, registry.SET_VALUE) require.NoError(t, err, "Should create test interface registry key") testKey.Close() @@ -56,7 +56,7 @@ func TestNRPTEntriesCleanupOnConfigChange(t *testing.T) { require.NoError(t, err) // Verify 3 NRPT rules exist - assert.Equal(t, 3, cfg.nrptEntryCount, "Should create 3 NRPT rules for 125 domains") + assert.Equal(t, 3, countNRPTRuleKeys(t), "Should create 3 NRPT rules for 125 domains") for i := 0; i < 3; i++ { exists, err := registryKeyExists(fmt.Sprintf("%s-%d", dnsPolicyConfigMatchPath, i)) require.NoError(t, err) @@ -81,7 +81,7 @@ func TestNRPTEntriesCleanupOnConfigChange(t *testing.T) { require.NoError(t, err) // Verify first 2 NRPT rules exist - assert.Equal(t, 2, cfg.nrptEntryCount, "Should create 2 NRPT rules for 75 domains") + assert.Equal(t, 2, countNRPTRuleKeys(t), "Should create 2 NRPT rules for 75 domains") for i := 0; i < 2; i++ { exists, err := registryKeyExists(fmt.Sprintf("%s-%d", dnsPolicyConfigMatchPath, i)) require.NoError(t, err) @@ -106,9 +106,65 @@ func registryKeyExists(path string) (bool, error) { return true, nil } +// TestNRPTCleanupWithoutRuleCount verifies that rules written by a previous run +// are removed by a configurator that has no record of how many there are: an +// unclean exit loses the in-memory count and a clean disconnect deletes the +// persisted one, so cleanup cannot depend on either. +func TestNRPTCleanupWithoutRuleCount(t *testing.T) { + if testing.Short() { + t.Skip("skipping registry integration test in short mode") + } + + defer cleanupRegistryKeys(t) + cleanupRegistryKeys(t) + + testIP := netip.MustParseAddr("100.64.0.1") + + // 75 domains produce two indexed rules, as the current layout does + domains := make([]string, 75) + for i := range domains { + domains[i] = fmt.Sprintf(".domain%d.com", i+1) + } + + previousRun := ®istryConfigurator{} + require.NoError(t, previousRun.addDNSMatchPolicy(domains, testIP)) + + // the unsuffixed key an older version would have written + require.NoError(t, previousRun.configureDNSPolicy(dnsPolicyConfigMatchPath, []string{".legacy.example.com"}, testIP)) + + // a policy owned by someone else, which cleanup must not touch + foreignPath := DNSPolicyConfigRoot + `\DnsPolicyConfigTestForeign` + foreignKey, _, err := registry.CreateKey(registry.LOCAL_MACHINE, foreignPath, registry.SET_VALUE) + require.NoError(t, err, "Should create foreign policy key") + foreignKey.Close() + defer func() { + _ = registry.DeleteKey(registry.LOCAL_MACHINE, foreignPath) + }() + + require.Equal(t, 3, countNRPTRuleKeys(t), "Should have two indexed rules and the legacy one") + + // a configurator that never applied a DNS config, as one built after a + // restart or from a shutdown state without a count is + freshRun := ®istryConfigurator{} + require.NoError(t, freshRun.removeDNSMatchPolicies()) + + assert.Equal(t, 0, countNRPTRuleKeys(t), "Should remove every rule left by the previous run") + + exists, err := registryKeyExists(foreignPath) + require.NoError(t, err) + assert.True(t, exists, "Should not remove a policy that is not ours") +} + +func countNRPTRuleKeys(t *testing.T) int { + t.Helper() + + names, err := listNRPTRuleKeys(DNSPolicyConfigRoot) + require.NoError(t, err, "Should list NRPT rule keys") + return len(names) +} + func cleanupRegistryKeys(*testing.T) { - // Clean up more entries to account for batching tests with many domains - cfg := ®istryConfigurator{nrptEntryCount: 20} + cfg := ®istryConfigurator{} _ = cfg.removeDNSMatchPolicies() } @@ -125,7 +181,7 @@ func TestNRPTDomainBatching(t *testing.T) { // Create a test interface registry key so updateSearchDomains doesn't fail testGUID := "{12345678-1234-1234-1234-123456789ABC}" - interfacePath := `SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\` + testGUID + interfacePath := InterfaceConfigPath + `\` + testGUID testKey, _, err := registry.CreateKey(registry.LOCAL_MACHINE, interfacePath, registry.SET_VALUE) require.NoError(t, err, "Should create test interface registry key") testKey.Close() @@ -193,7 +249,7 @@ func TestNRPTDomainBatching(t *testing.T) { require.NoError(t, err) // Verify that exactly expectedRuleCount rules were created - assert.Equal(t, tc.expectedRuleCount, cfg.nrptEntryCount, + assert.Equal(t, tc.expectedRuleCount, countNRPTRuleKeys(t), "Should create %d NRPT rules for %d domains", tc.expectedRuleCount, tc.domainCount) // Verify all expected rules exist diff --git a/client/internal/dns/response_writer_test.go b/client/internal/dns/response_writer_test.go index 857964406..bc8416029 100644 --- a/client/internal/dns/response_writer_test.go +++ b/client/internal/dns/response_writer_test.go @@ -4,7 +4,7 @@ import ( "net" "testing" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/miekg/dns" diff --git a/client/internal/dns/server_privileged_test.go b/client/internal/dns/server_privileged_test.go index a03aea169..a17044cf5 100644 --- a/client/internal/dns/server_privileged_test.go +++ b/client/internal/dns/server_privileged_test.go @@ -9,7 +9,7 @@ import ( "os" "testing" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/miekg/dns" "github.com/stretchr/testify/assert" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" diff --git a/client/internal/dns/unclean_shutdown_windows.go b/client/internal/dns/unclean_shutdown_windows.go index 24a9eca50..ab0b2cc63 100644 --- a/client/internal/dns/unclean_shutdown_windows.go +++ b/client/internal/dns/unclean_shutdown_windows.go @@ -5,9 +5,8 @@ import ( ) type ShutdownState struct { - Guid string - GPO bool - NRPTEntryCount int + Guid string + GPO bool } func (s *ShutdownState) Name() string { @@ -16,9 +15,8 @@ func (s *ShutdownState) Name() string { func (s *ShutdownState) Cleanup() error { manager := ®istryConfigurator{ - guid: s.Guid, - gpo: s.GPO, - nrptEntryCount: s.NRPTEntryCount, + guid: s.Guid, + gpo: s.GPO, } if err := manager.restoreUncleanShutdownDNS(); err != nil { diff --git a/client/internal/dnsfwd/manager.go b/client/internal/dnsfwd/manager.go index c4c16cd3f..29ca0d247 100644 --- a/client/internal/dnsfwd/manager.go +++ b/client/internal/dnsfwd/manager.go @@ -101,7 +101,7 @@ func (m *Manager) Start(fwdEntries []*ForwarderEntry) error { m.dnsForwarder = NewDNSForwarder(listenAddress, dnsTTL, m.firewall, m.statusRecorder, m.wgIface) go func() { - if err := m.dnsForwarder.Listen(fwdEntries); err != nil { + if err := m.dnsForwarder.Listen(fwdEntries); err != nil { //nolint:staticcheck // todo handle close error if it is exists log.Errorf("failed to start DNS forwarder, err: %v", err) } diff --git a/client/internal/ebpf/ebpf/manager_linux.go b/client/internal/ebpf/ebpf/manager_linux.go index 7520a6387..64a3e5b54 100644 --- a/client/internal/ebpf/ebpf/manager_linux.go +++ b/client/internal/ebpf/ebpf/manager_linux.go @@ -2,17 +2,21 @@ package ebpf import ( _ "embed" + "fmt" "net" "sync" "github.com/cilium/ebpf/link" "github.com/cilium/ebpf/rlimit" log "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" "github.com/netbirdio/netbird/client/internal/ebpf/manager" ) const ( + xdpProgName = "nb_xdp_prog" + mapKeyFeatures uint32 = 0 featureFlagWGProxy = 0b00000001 @@ -68,21 +72,50 @@ func (tf *GeneralManager) loadXdp() error { return err } - // load pre-compiled programs into the kernel. - err = loadBpfObjects(&tf.bpfObjs, nil) + // lo has no native XDP, so the program runs in generic mode. Unless it + // declares multi-buffer support the kernel must linearize every non-linear + // skb before running it. Loopback packets are up to 64 KB, so that is a + // contiguous GFP_ATOMIC allocation per packet, and when it fails the packet + // is dropped before the program runs, stalling local TCP connections. + // Multi-buffer XDP in generic mode requires kernel 6.3, so fall back to a + // plain attach when the kernel rejects it. + err = tf.attachXdp(iFace.Index, true) + if err == nil { + return nil + } + log.Debugf("failed to attach multi-buffer xdp program, retrying without it: %s", err) + + return tf.attachXdp(iFace.Index, false) +} + +func (tf *GeneralManager) attachXdp(iFaceIndex int, multiBuffer bool) error { + spec, err := loadBpf() if err != nil { - return err + return fmt.Errorf("load bpf spec: %w", err) + } + + if multiBuffer { + prog, ok := spec.Programs[xdpProgName] + if !ok { + return fmt.Errorf("program %s not found in bpf spec", xdpProgName) + } + prog.Flags |= unix.BPF_F_XDP_HAS_FRAGS + } + + if err := spec.LoadAndAssign(&tf.bpfObjs, nil); err != nil { + return fmt.Errorf("load bpf objects: %w", err) } tf.link, err = link.AttachXDP(link.XDPOptions{ Program: tf.bpfObjs.NbXdpProg, - Interface: iFace.Index, + Interface: iFaceIndex, }) - if err != nil { - _ = tf.bpfObjs.Close() + if closeErr := tf.bpfObjs.Close(); closeErr != nil { + log.Debugf("failed to close bpf objects after xdp attach error: %s", closeErr) + } tf.link = nil - return err + return fmt.Errorf("attach xdp: %w", err) } return nil } diff --git a/client/internal/engine.go b/client/internal/engine.go index d92c360f2..7f3f8185f 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -59,6 +59,7 @@ import ( "github.com/netbirdio/netbird/client/internal/syncstore" "github.com/netbirdio/netbird/client/internal/updater" "github.com/netbirdio/netbird/client/jobexec" + "github.com/netbirdio/netbird/client/netstate" cProto "github.com/netbirdio/netbird/client/proto" "github.com/netbirdio/netbird/client/system" nbdns "github.com/netbirdio/netbird/dns" @@ -181,6 +182,9 @@ type EngineServices struct { UpdateManager *updater.Manager ClientMetrics *metrics.ClientMetrics MetricsCtx context.Context + // NetState gates the reconnection loops on OS-reported network + // availability; nil disables gating. + NetState *netstate.State } // Engine is a mechanism responsible for reacting on Signal and Management stream events and managing connections to the remote peers. @@ -204,6 +208,10 @@ type Engine struct { config *EngineConfig mobileDep MobileDependency + // netState gates the peer reconnection guards on OS-reported network + // availability; nil disables gating. + netState *netstate.State + // STUNs is a list of STUN servers used by ICE STUNs []*stun.URI // TURNs is a list of STUN servers used by ICE @@ -337,6 +345,7 @@ func NewEngine( syncMsgMux: &sync.Mutex{}, config: config, mobileDep: mobileDep, + netState: services.NetState, STUNs: []*stun.URI{}, TURNs: []*stun.URI{}, networkSerial: 0, @@ -1893,7 +1902,8 @@ func (e *Engine) createPeerConn(pubKey string, allowedIPs []netip.Prefix, agentV Addr: e.getRosenpassAddr(), PermissiveMode: e.config.RosenpassPermissive, }, - ICEConfig: e.createICEConfig(), + ICEConfig: e.createICEConfig(), + NetworkState: e.netState, } serviceDependencies := peer.ServiceDependencies{ @@ -2562,7 +2572,7 @@ func (e *Engine) SetCapture(pc device.PacketCapture) error { } afc := capture.NewAFPacketCapture(intf.Name(), sess) - if err := afc.Start(); err != nil { + if err := afc.Start(); err != nil { //nolint:staticcheck // always errors on non-Linux builds return fmt.Errorf("start AF_PACKET capture on %s: %w", intf.Name(), err) } e.afpacketCapture = afc diff --git a/client/internal/engine_privileged_test.go b/client/internal/engine_privileged_test.go index f787f741f..032992464 100644 --- a/client/internal/engine_privileged_test.go +++ b/client/internal/engine_privileged_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/google/uuid" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" diff --git a/client/internal/peer/conn.go b/client/internal/peer/conn.go index f3235ec7f..b84b05671 100644 --- a/client/internal/peer/conn.go +++ b/client/internal/peer/conn.go @@ -26,6 +26,7 @@ import ( "github.com/netbirdio/netbird/client/internal/portforward" "github.com/netbirdio/netbird/client/internal/rosenpass" "github.com/netbirdio/netbird/client/internal/stdnet" + "github.com/netbirdio/netbird/client/netstate" "github.com/netbirdio/netbird/route" relayClient "github.com/netbirdio/netbird/shared/relay/client" ) @@ -93,6 +94,10 @@ type ConnConfig struct { // ICEConfig ICE protocol configuration ICEConfig icemaker.Config + + // NetworkState gates the reconnection guard on OS-reported network + // availability; nil disables gating. + NetworkState *netstate.State } type Conn struct { @@ -254,7 +259,7 @@ func (conn *Conn) open(engineCtx context.Context, firstPacket []byte) error { conn.handshaker.AddICEListener(conn.workerICE.OnNewOffer) } - conn.guard = guard.NewGuard(conn.Log, conn.isConnectedOnAllWay, conn.config.Timeout, conn.srWatcher) + conn.guard = guard.NewGuard(conn.Log, conn.isConnectedOnAllWay, conn.config.Timeout, conn.srWatcher, conn.config.NetworkState) conn.wg.Add(1) go func() { @@ -440,7 +445,7 @@ func (conn *Conn) onICEConnectionIsReady(priority conntype.ConnPriority, iceConn conn.dumpState.NewLocalProxy() wgProxy, err = conn.newProxy(iceConnInfo.RemoteConn) if err != nil { - conn.Log.Errorf("failed to add turn net.Conn to local proxy: %v", err) + conn.Log.Errorf("failed to add relayed net.Conn to local proxy: %v", err) return } ep = wgProxy.EndpointAddr() @@ -878,9 +883,8 @@ func (conn *Conn) newProxy(remoteConn net.Conn) (wgproxy.Proxy, error) { } wgProxy := conn.config.WgConfig.WgInterface.GetProxy() - if err := wgProxy.AddTurnConn(conn.ctx, udpAddr, remoteConn); err != nil { - conn.Log.Errorf("failed to add turn net.Conn to local proxy: %v", err) - return nil, err + if err := wgProxy.AddRelayedConn(conn.ctx, udpAddr, remoteConn); err != nil { + return nil, fmt.Errorf("add relayed conn to proxy: %w", err) } return wgProxy, nil } diff --git a/client/internal/peer/guard/guard.go b/client/internal/peer/guard/guard.go index 6c2e846a9..68d77d318 100644 --- a/client/internal/peer/guard/guard.go +++ b/client/internal/peer/guard/guard.go @@ -6,6 +6,8 @@ import ( "github.com/cenkalti/backoff/v4" log "github.com/sirupsen/logrus" + + "github.com/netbirdio/netbird/client/netstate" ) // ConnStatus represents the connection state as seen by the guard. @@ -31,20 +33,26 @@ type connStatusFunc func() ConnStatus // - Relayed connection disconnected // - ICE candidate changes type Guard struct { - log *log.Entry - isConnectedOnAllWay connStatusFunc - timeout time.Duration - srWatcher *SRWatcher + log *log.Entry + isConnectedOnAllWay connStatusFunc + timeout time.Duration + srWatcher *SRWatcher + // netState gates reconnect attempts on OS-reported network availability; + // nil disables gating. + netState *netstate.State relayedConnDisconnected chan struct{} iCEConnDisconnected chan struct{} } -func NewGuard(log *log.Entry, isConnectedFn connStatusFunc, timeout time.Duration, srWatcher *SRWatcher) *Guard { +// NewGuard creates a reconnection guard for a peer connection. A nil netState +// disables network availability gating. +func NewGuard(log *log.Entry, isConnectedFn connStatusFunc, timeout time.Duration, srWatcher *SRWatcher, netState *netstate.State) *Guard { return &Guard{ log: log, isConnectedOnAllWay: isConnectedFn, timeout: timeout, srWatcher: srWatcher, + netState: netState, relayedConnDisconnected: make(chan struct{}, 1), iCEConnDisconnected: make(chan struct{}, 1), } @@ -96,9 +104,16 @@ func (g *Guard) reconnectLoopWithRetry(ctx context.Context, callback func()) { iceState := &iceRetryState{log: g.log} defer iceState.reset() + netChanged := g.netState.Changed() + for { select { case <-tickerChannel: + // skip attempts while the OS reports no usable network; the + // netChanged case below resumes the loop once it returns + if !g.netState.IsOnline() { + continue + } switch g.isConnectedOnAllWay() { case ConnStatusConnected: // all good, nothing to do @@ -135,6 +150,23 @@ func (g *Guard) reconnectLoopWithRetry(ctx context.Context, callback func()) { tickerChannel = ticker.C iceState.reset() + case <-netChanged: + // Re-arm for the next transition before acting on this one. + netChanged = g.netState.Changed() + if !g.netState.IsOnline() { + continue + } + // Ticks skipped while offline drove the backoff towards its + // maximum without ever attempting, and left the ICE budget + // frozen — possibly in hourly mode. Recover on our own so the + // peer does not depend on a signal or relay event that never + // comes when both stayed up across the outage. + g.log.Debugf("network is back, reset reconnection ticker") + ticker.Stop() + ticker = g.newReconnectTicker(ctx) + tickerChannel = ticker.C + iceState.reset() + case <-ctx.Done(): g.log.Debugf("context is done, stop reconnect loop") return diff --git a/client/internal/peer/guard/guard_leak_test.go b/client/internal/peer/guard/guard_leak_test.go index ded3e4aea..3d82ec591 100644 --- a/client/internal/peer/guard/guard_leak_test.go +++ b/client/internal/peer/guard/guard_leak_test.go @@ -15,7 +15,7 @@ import ( func newTestGuard(status connStatusFunc) *Guard { srw := NewSRWatcher(nil, nil, nil, ice.Config{}) - return NewGuard(log.WithField("test", "guard"), status, 50*time.Millisecond, srw) + return NewGuard(log.WithField("test", "guard"), status, 50*time.Millisecond, srw, nil) } // countBackoffTickerGoroutines returns how many goroutines are currently sitting diff --git a/client/internal/peer/guard/guard_netstate_test.go b/client/internal/peer/guard/guard_netstate_test.go new file mode 100644 index 000000000..2ab736428 --- /dev/null +++ b/client/internal/peer/guard/guard_netstate_test.go @@ -0,0 +1,107 @@ +package guard + +import ( + "context" + "sync/atomic" + "testing" + "time" + + log "github.com/sirupsen/logrus" + + "github.com/netbirdio/netbird/client/internal/peer/ice" + "github.com/netbirdio/netbird/client/netstate" +) + +// newTestGuardWithNetState builds a guard with a realistic MaxInterval: the +// backoff must be able to grow well past the outage, as it does in production +// where the timeout is seconds to minutes. +func newTestGuardWithNetState(status connStatusFunc, netState *netstate.State) *Guard { + srw := NewSRWatcher(nil, nil, nil, ice.Config{}) + return NewGuard(log.WithField("test", "guard"), status, 30*time.Second, srw, netState) +} + +// TestGuard_RecoversAfterOfflineToOnline covers a peer that stays disconnected +// across a network outage while neither signal nor relay reports an event — +// both stayed up, as on a short airplane mode toggle over Wi-Fi. +// +// Every tick taken while offline is skipped, but it still advances the +// exponential backoff, so by the time the network returns the next tick can be +// tens of seconds away. Without an explicit reaction to the transition the +// peer waits out that interval for a recovery that could start immediately. +func TestGuard_RecoversAfterOfflineToOnline(t *testing.T) { + netState := netstate.New() + + var attempts atomic.Int32 + g := newTestGuardWithNetState(func() ConnStatus { return ConnStatusDisconnected }, netState) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + // Start from the reconnect ticker (800ms initial interval), the state a + // peer is in after it loses its connection. + go g.Start(ctx, func() { attempts.Add(1) }) + g.SetRelayedConnDisconnected() + + // Let the backoff climb: 0.8s, 1.6s, 3.2s, 6.4s ... every tick is skipped + // while offline, but each one doubles the wait for the next. + netState.Set(false) + time.Sleep(8 * time.Second) + + offlineAttempts := attempts.Load() + if offlineAttempts != 0 { + t.Fatalf("callback ran %d times while offline, want 0", offlineAttempts) + } + + netState.Set(true) + + // The next organic tick is now several seconds out, so anything within + // this window can only come from reacting to the transition itself. + pollCtx, stopPolling := context.WithTimeout(ctx, 2*time.Second) + defer stopPolling() + + select { + case <-pollCtx.Done(): + t.Fatal("peer was not retried within 2s of the network coming back, " + + "with neither a signal nor a relay event to fall back on") + case <-pollUntil(pollCtx, func() bool { return attempts.Load() > 0 }): + } +} + +// TestGuard_OfflineTransitionDoesNotRetry checks the other direction: going +// offline must not itself trigger an attempt. +func TestGuard_OfflineTransitionDoesNotRetry(t *testing.T) { + netState := netstate.New() + + var attempts atomic.Int32 + g := newTestGuardWithNetState(func() ConnStatus { return ConnStatusDisconnected }, netState) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + go g.Start(ctx, func() { attempts.Add(1) }) + + netState.Set(false) + time.Sleep(5 * time.Second) + + if got := attempts.Load(); got != 0 { + t.Fatalf("callback ran %d times after going offline, want 0", got) + } +} + +// pollUntil closes the returned channel once cond holds. It gives up when ctx +// is done, so the polling goroutine never outlives the test that started it. +func pollUntil(ctx context.Context, cond func() bool) <-chan struct{} { + done := make(chan struct{}) + go func() { + for { + if cond() { + close(done) + return + } + select { + case <-ctx.Done(): + return + case <-time.After(10 * time.Millisecond): + } + } + }() + return done +} diff --git a/client/internal/peer/listener.go b/client/internal/peer/listener.go index c601fe534..2bb7fcf32 100644 --- a/client/internal/peer/listener.go +++ b/client/internal/peer/listener.go @@ -1,11 +1,40 @@ package peer +// ClientState identifies the client connection state delivered via +// Listener.OnStateChanged. +type ClientState int + +// Client states. The numeric values cross the gomobile boundary (the mobile +// bindings re-export them as integer constants), so they are a wire format: +// append new states at the end, never reorder or insert. +const ( + ClientStateDisconnected ClientState = iota + ClientStateConnected + ClientStateConnecting + ClientStateDisconnecting + // ClientStateNoNetwork is an overlay state: it is never stored as the + // last notification, only derived from ClientStateConnecting while the + // OS reports no usable network (see notifier.effectiveState). + ClientStateNoNetwork +) + // Listener is a callback type about the NetBird network connection state type Listener interface { + // OnStateChanged reports every client state transition. New states are + // delivered only through this callback; the per-state callbacks below + // are kept for compatibility and will be removed once all consumers + // have migrated. + OnStateChanged(state ClientState) + + // Deprecated: consume OnStateChanged instead. OnConnected() + // Deprecated: consume OnStateChanged instead. OnDisconnected() + // Deprecated: consume OnStateChanged instead. OnConnecting() + // Deprecated: consume OnStateChanged instead. OnDisconnecting() + OnAddressChanged(string, string) OnPeersListChanged(int) } diff --git a/client/internal/peer/notifier.go b/client/internal/peer/notifier.go index 8d1954fe5..1ee1d32ea 100644 --- a/client/internal/peer/notifier.go +++ b/client/internal/peer/notifier.go @@ -4,31 +4,64 @@ import ( "sync" ) -const ( - stateDisconnected = iota - stateConnected - stateConnecting - stateDisconnecting -) - type notifier struct { + // publishLock orders state publication: it is held across computing the + // effective state and handing it to the listener, so a transition cannot + // overtake a newer one and leave the listener on a stale state. + publishLock sync.Mutex serverStateLock sync.Mutex listenersLock sync.Mutex listener Listener currentClientState bool - lastNotification int + lastNotification ClientState lastNumberOfPeers int lastFqdnAddress string lastIPAddress string + networkAvailable bool } func newNotifier() *notifier { - return ¬ifier{} + return ¬ifier{ + networkAvailable: true, + } +} + +// effectiveState maps the computed state to what listeners should see: +// while the OS reports no usable network, "Connecting" would be a lie — +// connection attempts are suspended — so it is reported as NoNetwork. +// Caller must hold serverStateLock. +func (n *notifier) effectiveState(state ClientState) ClientState { + if !n.networkAvailable && state == ClientStateConnecting { + return ClientStateNoNetwork + } + return state +} + +// setNetworkAvailable records the OS network availability and re-notifies +// the listener when the flag flips the effective state (Connecting <-> +// NoNetwork). +func (n *notifier) setNetworkAvailable(available bool) { + n.publishLock.Lock() + defer n.publishLock.Unlock() + + n.serverStateLock.Lock() + if n.networkAvailable == available { + n.serverStateLock.Unlock() + return + } + previous := n.effectiveState(n.lastNotification) + n.networkAvailable = available + current := n.effectiveState(n.lastNotification) + n.serverStateLock.Unlock() + + if previous != current { + n.notify(current) + } } func (n *notifier) setListener(listener Listener) { n.serverStateLock.Lock() - lastNotification := n.lastNotification + lastNotification := n.effectiveState(n.lastNotification) numOfPeers := n.lastNumberOfPeers fqdnAddress := n.lastFqdnAddress address := n.lastIPAddress @@ -52,6 +85,9 @@ func (n *notifier) removeListener() { } func (n *notifier) updateServerStates(mgmState bool, signalState bool) { + n.publishLock.Lock() + defer n.publishLock.Unlock() + n.serverStateLock.Lock() calculatedState := n.calculateState(mgmState, signalState) @@ -61,43 +97,54 @@ func (n *notifier) updateServerStates(mgmState bool, signalState bool) { } n.lastNotification = calculatedState + effective := n.effectiveState(calculatedState) n.serverStateLock.Unlock() - n.notify(calculatedState) + n.notify(effective) } func (n *notifier) clientStart() { + n.publishLock.Lock() + defer n.publishLock.Unlock() + n.serverStateLock.Lock() n.currentClientState = true - n.lastNotification = stateConnecting + n.lastNotification = ClientStateConnecting + effective := n.effectiveState(ClientStateConnecting) n.serverStateLock.Unlock() - n.notify(stateConnecting) + n.notify(effective) } func (n *notifier) clientStop() { + n.publishLock.Lock() + defer n.publishLock.Unlock() + n.serverStateLock.Lock() n.currentClientState = false - n.lastNotification = stateDisconnected + n.lastNotification = ClientStateDisconnected n.serverStateLock.Unlock() - n.notify(stateDisconnected) + n.notify(ClientStateDisconnected) } func (n *notifier) clientTearDown() { + n.publishLock.Lock() + defer n.publishLock.Unlock() + n.serverStateLock.Lock() n.currentClientState = false - n.lastNotification = stateDisconnecting + n.lastNotification = ClientStateDisconnecting n.serverStateLock.Unlock() - n.notify(stateDisconnecting) + n.notify(ClientStateDisconnecting) } -func (n *notifier) isServerStateChanged(newState int) bool { +func (n *notifier) isServerStateChanged(newState ClientState) bool { return n.lastNotification != newState } -func (n *notifier) notify(state int) { +func (n *notifier) notify(state ClientState) { n.listenersLock.Lock() listener := n.listener n.listenersLock.Unlock() @@ -109,20 +156,20 @@ func (n *notifier) notify(state int) { notifyListener(listener, state) } -func (n *notifier) calculateState(managementConn, signalConn bool) int { +func (n *notifier) calculateState(managementConn, signalConn bool) ClientState { if managementConn && signalConn { - return stateConnected + return ClientStateConnected } if !managementConn && !signalConn && !n.currentClientState { - return stateDisconnected + return ClientStateDisconnected } - if n.lastNotification == stateDisconnecting { - return stateDisconnecting + if n.lastNotification == ClientStateDisconnecting { + return ClientStateDisconnecting } - return stateConnecting + return ClientStateConnecting } func (n *notifier) peerListChanged(numOfPeers int) { @@ -159,15 +206,19 @@ func (n *notifier) localAddressChanged(fqdn, address string) { listener.OnAddressChanged(fqdn, address) } -func notifyListener(l Listener, state int) { +func notifyListener(l Listener, state ClientState) { + // legacy per-state callbacks; NoNetwork is delivered only via + // OnStateChanged below switch state { - case stateDisconnected: + case ClientStateDisconnected: l.OnDisconnected() - case stateConnected: + case ClientStateConnected: l.OnConnected() - case stateConnecting: + case ClientStateConnecting: l.OnConnecting() - case stateDisconnecting: + case ClientStateDisconnecting: l.OnDisconnecting() } + + l.OnStateChanged(state) } diff --git a/client/internal/peer/notifier_concurrent_test.go b/client/internal/peer/notifier_concurrent_test.go new file mode 100644 index 000000000..fcaaaad3b --- /dev/null +++ b/client/internal/peer/notifier_concurrent_test.go @@ -0,0 +1,108 @@ +package peer + +import ( + "sync" + "testing" + "time" +) + +type recordingListener struct { + mu sync.Mutex + states []ClientState + onState func(ClientState) +} + +func (l *recordingListener) OnStateChanged(state ClientState) { + l.mu.Lock() + l.states = append(l.states, state) + hook := l.onState + l.mu.Unlock() + + if hook != nil { + hook(state) + } +} + +func (l *recordingListener) last() (ClientState, bool) { + l.mu.Lock() + defer l.mu.Unlock() + if len(l.states) == 0 { + return 0, false + } + return l.states[len(l.states)-1], true +} + +func (l *recordingListener) snapshot() []ClientState { + l.mu.Lock() + defer l.mu.Unlock() + return append([]ClientState(nil), l.states...) +} + +func (l *recordingListener) OnConnected() {} +func (l *recordingListener) OnDisconnected() {} +func (l *recordingListener) OnConnecting() {} +func (l *recordingListener) OnDisconnecting() {} +func (l *recordingListener) OnAddressChanged(string, string) {} +func (l *recordingListener) OnPeersListChanged(int) {} + +// TestNotifier_ConcurrentAvailabilityFlipOrdersPublication holds the first +// transition inside the listener callback and flips availability again from +// another goroutine while it is parked. The second flip must not publish +// ahead of the one in flight, otherwise the listener ends up on a state the +// notifier already superseded. +func TestNotifier_ConcurrentAvailabilityFlipOrdersPublication(t *testing.T) { + n := newNotifier() + n.currentClientState = true + n.lastNotification = ClientStateConnecting + + entered := make(chan struct{}) + release := make(chan struct{}) + + l := &recordingListener{} + l.onState = func(state ClientState) { + if state != ClientStateNoNetwork { + return + } + l.mu.Lock() + l.onState = nil + l.mu.Unlock() + close(entered) + <-release + } + n.listener = l + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + n.setNetworkAvailable(false) + }() + + <-entered + + flipped := make(chan struct{}) + go func() { + defer close(flipped) + n.setNetworkAvailable(true) + }() + + select { + case <-flipped: + t.Fatal("the online transition published while the offline one was " + + "still in flight; publication is not serialized") + case <-time.After(200 * time.Millisecond): + } + + close(release) + <-flipped + wg.Wait() + + got, ok := l.last() + if !ok { + t.Fatal("listener never observed a state") + } + if got != ClientStateConnecting { + t.Fatalf("listener holds %v after the network came back, want Connecting; sequence: %v", + got, l.snapshot()) + } +} diff --git a/client/internal/peer/notifier_test.go b/client/internal/peer/notifier_test.go index 0b7722b0c..a73016b05 100644 --- a/client/internal/peer/notifier_test.go +++ b/client/internal/peer/notifier_test.go @@ -6,29 +6,32 @@ import ( ) type mocListener struct { - lastState int + lastState ClientState wg sync.WaitGroup peersWg sync.WaitGroup peers int } func (l *mocListener) OnConnected() { - l.lastState = stateConnected + l.lastState = ClientStateConnected l.wg.Done() } func (l *mocListener) OnDisconnected() { - l.lastState = stateDisconnected + l.lastState = ClientStateDisconnected l.wg.Done() } func (l *mocListener) OnConnecting() { - l.lastState = stateConnecting + l.lastState = ClientStateConnecting l.wg.Done() } func (l *mocListener) OnDisconnecting() { - l.lastState = stateDisconnecting + l.lastState = ClientStateDisconnecting l.wg.Done() } +func (l *mocListener) OnStateChanged(state ClientState) { + +} func (l *mocListener) OnAddressChanged(host, addr string) { } @@ -57,15 +60,15 @@ func Test_notifier_serverState(t *testing.T) { type scenario struct { name string - expected int + expected ClientState mgmState bool signalState bool } scenarios := []scenario{ - {"connected", stateConnected, true, true}, - {"mgm down", stateConnecting, false, true}, - {"signal down", stateConnecting, true, false}, - {"disconnected", stateDisconnected, false, false}, + {"connected", ClientStateConnected, true, true}, + {"mgm down", ClientStateConnecting, false, true}, + {"signal down", ClientStateConnecting, true, false}, + {"disconnected", ClientStateDisconnected, false, false}, } for _, tt := range scenarios { @@ -85,7 +88,7 @@ func Test_notifier_SetListener(t *testing.T) { listener.setPeersWaiter() n := newNotifier() - n.lastNotification = stateConnecting + n.lastNotification = ClientStateConnecting n.setListener(listener) listener.wait() listener.waitPeers() @@ -99,7 +102,7 @@ func Test_notifier_RemoveListener(t *testing.T) { listener.setWaiter() listener.setPeersWaiter() n := newNotifier() - n.lastNotification = stateConnecting + n.lastNotification = ClientStateConnecting n.setListener(listener) // setListener replays cached state on a goroutine; wait for both the state // and peers callbacks to finish so we don't race on listener.peers. diff --git a/client/internal/peer/status.go b/client/internal/peer/status.go index 423ce9b23..24e3e7fac 100644 --- a/client/internal/peer/status.go +++ b/client/internal/peer/status.go @@ -1211,6 +1211,12 @@ func (d *Status) ClientTeardown() { d.notifyStateChange() } +// SetNetworkAvailable records the OS-reported network availability; while +// unavailable, listeners see NoNetwork instead of Connecting. +func (d *Status) SetNetworkAvailable(available bool) { + d.notifier.setNetworkAvailable(available) +} + // SetConnectionListener set a listener to the notifier func (d *Status) SetConnectionListener(listener Listener) { d.notifier.setListener(listener) diff --git a/client/internal/peer/worker_ice.go b/client/internal/peer/worker_ice.go index b1aa3e0f9..83cac13f5 100644 --- a/client/internal/peer/worker_ice.go +++ b/client/internal/peer/worker_ice.go @@ -255,8 +255,8 @@ func (w *WorkerICE) connect(ctx context.Context, agent *icemaker.ThreadSafeAgent return } - w.log.Debugf("turn agent dial") - remoteConn, err := w.turnAgentDial(ctx, agent, remoteOfferAnswer) + w.log.Debugf("agent dial") + remoteConn, err := w.agentDial(ctx, agent, remoteOfferAnswer) if err != nil { w.log.Debugf("failed to dial the remote peer: %s", err) w.closeAgent(agent, w.agentDialerCancel) @@ -389,6 +389,17 @@ func (w *WorkerICE) injectPortForwardedCandidate(srflxCandidate ice.Candidate) { return } + // A forwarded candidate only makes sense for an IPv4 mapping, which + // translates a port on the gateway's address. An IPv6 pinhole translates + // nothing: it unblocks the address ICE already gathers as a host candidate, + // so there is no second address to advertise. Injecting one here would also + // paste an IPv6 address onto whichever server-reflexive candidate arrived + // first, which is usually IPv4. + if mapping.ExternalIP != nil && mapping.ExternalIP.To4() == nil { + w.log.Debugf("skipping port-forwarded candidate: %s mapping is IPv6-only", mapping.NATType) + return + } + w.muxAgent.Lock() if w.portForwardAttempted { w.muxAgent.Unlock() @@ -517,8 +528,8 @@ func (w *WorkerICE) onConnectionStateChange(agent *icemaker.ThreadSafeAgent, dia w.logSuccessfulPaths(agent) return case ice.ConnectionStateFailed, ice.ConnectionStateDisconnected, ice.ConnectionStateClosed: - // ice.ConnectionStateClosed happens when we recreate the agent. For the P2P to TURN switch important to - // notify the conn.onICEStateDisconnected changes to update the current used priority + // ice.ConnectionStateClosed happens when we recreate the agent. The P2P to relay switch requires + // notifying conn.onICEStateDisconnected so it can update the currently used priority. sessionChanged := w.closeAgent(agent, dialerCancel) @@ -532,7 +543,7 @@ func (w *WorkerICE) onConnectionStateChange(agent *icemaker.ThreadSafeAgent, dia } } -func (w *WorkerICE) turnAgentDial(ctx context.Context, agent *icemaker.ThreadSafeAgent, remoteOfferAnswer *OfferAnswer) (*ice.Conn, error) { +func (w *WorkerICE) agentDial(ctx context.Context, agent *icemaker.ThreadSafeAgent, remoteOfferAnswer *OfferAnswer) (*ice.Conn, error) { if isController(w.config) { return agent.Dial(ctx, remoteOfferAnswer.IceCredentials.UFrag, remoteOfferAnswer.IceCredentials.Pwd) } else { diff --git a/client/internal/portforward/manager.go b/client/internal/portforward/manager.go index b0680160c..7d5a4cb9e 100644 --- a/client/internal/portforward/manager.go +++ b/client/internal/portforward/manager.go @@ -10,10 +10,8 @@ import ( "sync" "time" - "github.com/libp2p/go-nat" + "github.com/netbirdio/go-nat" log "github.com/sirupsen/logrus" - - "github.com/netbirdio/netbird/client/internal/portforward/pcp" ) const ( @@ -168,6 +166,11 @@ func (m *Manager) setup(ctx context.Context) (nat.NAT, *Mapping, error) { if err != nil { return nil, nil, fmt.Errorf("create port mapping: %w", err) } + + // Only meaningful once a mapping has been attempted: that is what opens the + // pinhole and records its outcome. + logIPv6Pinhole(gateway) + return gateway, mapping, nil } @@ -265,7 +268,9 @@ func (m *Manager) checkHealthAndRecreate(ctx context.Context, gateway nat.NAT) b return false } - pcpNAT, ok := gateway.(*pcp.NAT) + // Assert on the interface, not on a concrete type: a dual-stack gateway is + // a wrapper around the IPv4 NAT, so a type assertion misses it. + checker, ok := gateway.(nat.HealthChecker) if !ok { return false } @@ -273,7 +278,7 @@ func (m *Manager) checkHealthAndRecreate(ctx context.Context, gateway nat.NAT) b ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() - epoch, serverRestarted, err := pcpNAT.CheckServerHealth(ctx) + epoch, serverRestarted, err := checker.CheckServerHealth(ctx) if err != nil { log.Debugf("PCP health check failed: %v", err) return false @@ -340,3 +345,18 @@ func (m *Manager) startTearDown(ctx context.Context) { func isPermanentLeaseRequired(err error) bool { return err != nil && upnpErrPermanentLeaseOnly.MatchString(err.Error()) } + +// logIPv6Pinhole reports the outcome of the IPv6 pinhole. Pinholes are best +// effort and never fail a mapping on their own, so this is the only way to see +// whether one was actually opened. +func logIPv6Pinhole(gateway nat.NAT) { + reporter, ok := gateway.(nat.IPv6PinholeReporter) + if !ok { + return + } + if err := reporter.IPv6PinholeError(); err != nil { + log.Warnf("IPv6 pinhole: %v", err) + return + } + log.Infof("IPv6 pinhole open") +} diff --git a/client/internal/portforward/pcp/client.go b/client/internal/portforward/pcp/client.go deleted file mode 100644 index f6d243ef9..000000000 --- a/client/internal/portforward/pcp/client.go +++ /dev/null @@ -1,408 +0,0 @@ -package pcp - -import ( - "context" - "crypto/rand" - "errors" - "fmt" - "net" - "net/netip" - "sync" - "time" - - log "github.com/sirupsen/logrus" -) - -const ( - defaultTimeout = 3 * time.Second - responseBufferSize = 128 - - // RFC 6887 Section 8.1.1 retry timing - initialRetryDelay = 3 * time.Second - maxRetryDelay = 1024 * time.Second - maxRetries = 4 // 3s + 6s + 12s + 24s = 45s total worst case -) - -// Client is a PCP protocol client. -// All methods are safe for concurrent use. -type Client struct { - gateway netip.Addr - timeout time.Duration - - mu sync.Mutex - // localIP caches the resolved local IP address. - localIP netip.Addr - // lastEpoch is the last observed server epoch value. - lastEpoch uint32 - // epochTime tracks when lastEpoch was received for state loss detection. - epochTime time.Time - // externalIP caches the external IP from the last successful MAP response. - externalIP netip.Addr - // epochStateLost is set when epoch indicates server restart. - epochStateLost bool -} - -// NewClient creates a new PCP client for the gateway at the given IP. -func NewClient(gateway net.IP) *Client { - addr, ok := netip.AddrFromSlice(gateway) - if !ok { - log.Debugf("invalid gateway IP: %v", gateway) - } - return &Client{ - gateway: addr.Unmap(), - timeout: defaultTimeout, - } -} - -// NewClientWithTimeout creates a new PCP client with a custom timeout. -func NewClientWithTimeout(gateway net.IP, timeout time.Duration) *Client { - addr, ok := netip.AddrFromSlice(gateway) - if !ok { - log.Debugf("invalid gateway IP: %v", gateway) - } - return &Client{ - gateway: addr.Unmap(), - timeout: timeout, - } -} - -// SetLocalIP sets the local IP address to use in PCP requests. -func (c *Client) SetLocalIP(ip net.IP) { - addr, ok := netip.AddrFromSlice(ip) - if !ok { - log.Debugf("invalid local IP: %v", ip) - } - c.mu.Lock() - c.localIP = addr.Unmap() - c.mu.Unlock() -} - -// Gateway returns the gateway IP address. -func (c *Client) Gateway() net.IP { - return c.gateway.AsSlice() -} - -// Announce sends a PCP ANNOUNCE request to discover PCP support. -// Returns the server's epoch time on success. -func (c *Client) Announce(ctx context.Context) (epoch uint32, err error) { - localIP, err := c.getLocalIP() - if err != nil { - return 0, fmt.Errorf("get local IP: %w", err) - } - - req := buildAnnounceRequest(localIP) - resp, err := c.sendRequest(ctx, req) - if err != nil { - return 0, fmt.Errorf("send announce: %w", err) - } - - parsed, err := parseResponse(resp) - if err != nil { - return 0, fmt.Errorf("parse announce response: %w", err) - } - - if parsed.ResultCode != ResultSuccess { - return 0, fmt.Errorf("PCP ANNOUNCE failed: %s", ResultCodeString(parsed.ResultCode)) - } - - c.mu.Lock() - if c.updateEpochLocked(parsed.Epoch) { - log.Warnf("PCP server epoch indicates state loss - mappings may need refresh") - } - c.mu.Unlock() - return parsed.Epoch, nil -} - -// AddPortMapping requests a port mapping from the PCP server. -func (c *Client) AddPortMapping(ctx context.Context, protocol string, internalPort int, lifetime time.Duration) (*MapResponse, error) { - return c.addPortMappingWithHint(ctx, protocol, internalPort, internalPort, netip.Addr{}, lifetime) -} - -// AddPortMappingWithHint requests a port mapping with suggested external port and IP. -// Use lifetime <= 0 to delete a mapping. -func (c *Client) AddPortMappingWithHint(ctx context.Context, protocol string, internalPort, suggestedExtPort int, suggestedExtIP net.IP, lifetime time.Duration) (*MapResponse, error) { - var extIP netip.Addr - if suggestedExtIP != nil { - var ok bool - extIP, ok = netip.AddrFromSlice(suggestedExtIP) - if !ok { - log.Debugf("invalid suggested external IP: %v", suggestedExtIP) - } - extIP = extIP.Unmap() - } - return c.addPortMappingWithHint(ctx, protocol, internalPort, suggestedExtPort, extIP, lifetime) -} - -func (c *Client) addPortMappingWithHint(ctx context.Context, protocol string, internalPort, suggestedExtPort int, suggestedExtIP netip.Addr, lifetime time.Duration) (*MapResponse, error) { - localIP, err := c.getLocalIP() - if err != nil { - return nil, fmt.Errorf("get local IP: %w", err) - } - - proto, err := protocolNumber(protocol) - if err != nil { - return nil, fmt.Errorf("parse protocol: %w", err) - } - - var nonce [12]byte - if _, err := rand.Read(nonce[:]); err != nil { - return nil, fmt.Errorf("generate nonce: %w", err) - } - - // Convert lifetime to seconds. Lifetime 0 means delete, so only apply - // default for positive durations that round to 0 seconds. - var lifetimeSec uint32 - if lifetime > 0 { - lifetimeSec = uint32(lifetime.Seconds()) - if lifetimeSec == 0 { - lifetimeSec = DefaultLifetime - } - } - - req := buildMapRequest(localIP, nonce, proto, uint16(internalPort), uint16(suggestedExtPort), suggestedExtIP, lifetimeSec) - - resp, err := c.sendRequest(ctx, req) - if err != nil { - return nil, fmt.Errorf("send map request: %w", err) - } - - mapResp, err := parseMapResponse(resp) - if err != nil { - return nil, fmt.Errorf("parse map response: %w", err) - } - - if mapResp.Nonce != nonce { - return nil, fmt.Errorf("nonce mismatch in response") - } - - if mapResp.Protocol != proto { - return nil, fmt.Errorf("protocol mismatch: requested %d, got %d", proto, mapResp.Protocol) - } - if mapResp.InternalPort != uint16(internalPort) { - return nil, fmt.Errorf("internal port mismatch: requested %d, got %d", internalPort, mapResp.InternalPort) - } - - if mapResp.ResultCode != ResultSuccess { - return nil, &Error{ - Code: mapResp.ResultCode, - Message: ResultCodeString(mapResp.ResultCode), - } - } - - c.mu.Lock() - if c.updateEpochLocked(mapResp.Epoch) { - log.Warnf("PCP server epoch indicates state loss - mappings may need refresh") - } - c.cacheExternalIPLocked(mapResp.ExternalIP) - c.mu.Unlock() - return mapResp, nil -} - -// DeletePortMapping removes a port mapping by requesting zero lifetime. -func (c *Client) DeletePortMapping(ctx context.Context, protocol string, internalPort int) error { - if _, err := c.addPortMappingWithHint(ctx, protocol, internalPort, 0, netip.Addr{}, 0); err != nil { - var pcpErr *Error - if errors.As(err, &pcpErr) && pcpErr.Code == ResultNotAuthorized { - return nil - } - return fmt.Errorf("delete mapping: %w", err) - } - return nil -} - -// GetExternalAddress returns the external IP address. -// First checks for a cached value from previous MAP responses. -// If not cached, creates a short-lived mapping to discover the external IP. -func (c *Client) GetExternalAddress(ctx context.Context) (net.IP, error) { - c.mu.Lock() - if c.externalIP.IsValid() { - ip := c.externalIP.AsSlice() - c.mu.Unlock() - return ip, nil - } - c.mu.Unlock() - - // Use an ephemeral port in the dynamic range (49152-65535). - // Port 0 is not valid with UDP/TCP protocols per RFC 6887. - ephemeralPort := 49152 + int(uint16(time.Now().UnixNano()))%(65535-49152) - - // Use minimal lifetime (1 second) for discovery. - resp, err := c.AddPortMapping(ctx, "udp", ephemeralPort, time.Second) - if err != nil { - return nil, fmt.Errorf("create temporary mapping: %w", err) - } - - if err := c.DeletePortMapping(ctx, "udp", ephemeralPort); err != nil { - log.Debugf("cleanup temporary PCP mapping: %v", err) - } - - return resp.ExternalIP.AsSlice(), nil -} - -// LastEpoch returns the last observed server epoch value. -// A decrease in epoch indicates the server may have restarted and mappings may be lost. -func (c *Client) LastEpoch() uint32 { - c.mu.Lock() - defer c.mu.Unlock() - return c.lastEpoch -} - -// EpochStateLost returns true if epoch state loss was detected and clears the flag. -func (c *Client) EpochStateLost() bool { - c.mu.Lock() - defer c.mu.Unlock() - lost := c.epochStateLost - c.epochStateLost = false - return lost -} - -// updateEpoch updates the epoch tracking and detects potential state loss. -// Returns true if state loss was detected (server likely restarted). -// Caller must hold c.mu. -func (c *Client) updateEpochLocked(newEpoch uint32) bool { - now := time.Now() - stateLost := false - - // RFC 6887 Section 8.5: Detect invalid epoch indicating server state loss. - // client_delta = time since last response - // server_delta = epoch change since last response - // Invalid if: client_delta+2 < server_delta - server_delta/16 - // OR: server_delta+2 < client_delta - client_delta/16 - // The +2 handles quantization, /16 (6.25%) handles clock drift. - if !c.epochTime.IsZero() && c.lastEpoch > 0 { - clientDelta := uint32(now.Sub(c.epochTime).Seconds()) - serverDelta := newEpoch - c.lastEpoch - - // Check for epoch going backwards or jumping unexpectedly. - // Subtraction is safe: serverDelta/16 is always <= serverDelta. - if clientDelta+2 < serverDelta-(serverDelta/16) || - serverDelta+2 < clientDelta-(clientDelta/16) { - stateLost = true - c.epochStateLost = true - } - } - - c.lastEpoch = newEpoch - c.epochTime = now - return stateLost -} - -// cacheExternalIP stores the external IP from a successful MAP response. -// Caller must hold c.mu. -func (c *Client) cacheExternalIPLocked(ip netip.Addr) { - if ip.IsValid() && !ip.IsUnspecified() { - c.externalIP = ip - } -} - -// sendRequest sends a PCP request with retries per RFC 6887 Section 8.1.1. -func (c *Client) sendRequest(ctx context.Context, req []byte) ([]byte, error) { - addr := &net.UDPAddr{IP: c.gateway.AsSlice(), Port: Port} - - var lastErr error - delay := initialRetryDelay - - for range maxRetries { - resp, err := c.sendOnce(ctx, addr, req) - if err == nil { - return resp, nil - } - lastErr = err - - if ctx.Err() != nil { - return nil, ctx.Err() - } - - // RFC 6887 Section 8.1.1: RT = (1 + RAND) * MIN(2 * RTprev, MRT) - // RAND is random between -0.1 and +0.1 - select { - case <-ctx.Done(): - return nil, ctx.Err() - case <-time.After(retryDelayWithJitter(delay)): - } - delay = min(delay*2, maxRetryDelay) - } - - return nil, fmt.Errorf("PCP request failed after %d retries: %w", maxRetries, lastErr) -} - -// retryDelayWithJitter applies RFC 6887 jitter: multiply by (1 + RAND) where RAND is [-0.1, +0.1]. -func retryDelayWithJitter(d time.Duration) time.Duration { - var b [1]byte - _, _ = rand.Read(b[:]) - // Convert byte to range [-0.1, +0.1]: (b/255 * 0.2) - 0.1 - jitter := (float64(b[0])/255.0)*0.2 - 0.1 - return time.Duration(float64(d) * (1 + jitter)) -} - -func (c *Client) sendOnce(ctx context.Context, addr *net.UDPAddr, req []byte) ([]byte, error) { - // Use ListenUDP instead of DialUDP to validate response source address per RFC 6887 §8.3. - conn, err := net.ListenUDP("udp", nil) - if err != nil { - return nil, fmt.Errorf("listen: %w", err) - } - defer func() { - if err := conn.Close(); err != nil { - log.Debugf("close UDP connection: %v", err) - } - }() - - timeout := c.timeout - if deadline, ok := ctx.Deadline(); ok { - if remaining := time.Until(deadline); remaining < timeout { - timeout = remaining - } - } - - if err := conn.SetDeadline(time.Now().Add(timeout)); err != nil { - return nil, fmt.Errorf("set deadline: %w", err) - } - - if _, err := conn.WriteToUDP(req, addr); err != nil { - return nil, fmt.Errorf("write: %w", err) - } - - resp := make([]byte, responseBufferSize) - n, from, err := conn.ReadFromUDP(resp) - if err != nil { - return nil, fmt.Errorf("read: %w", err) - } - - // RFC 6887 §8.3: Validate response came from expected PCP server. - if !from.IP.Equal(addr.IP) { - return nil, fmt.Errorf("response from unexpected source %s (expected %s)", from.IP, addr.IP) - } - - return resp[:n], nil -} - -func (c *Client) getLocalIP() (netip.Addr, error) { - c.mu.Lock() - defer c.mu.Unlock() - - if !c.localIP.IsValid() { - return netip.Addr{}, fmt.Errorf("local IP not set for gateway %s", c.gateway) - } - return c.localIP, nil -} - -func protocolNumber(protocol string) (uint8, error) { - switch protocol { - case "udp", "UDP": - return ProtoUDP, nil - case "tcp", "TCP": - return ProtoTCP, nil - default: - return 0, fmt.Errorf("unsupported protocol: %s", protocol) - } -} - -// Error represents a PCP error response. -type Error struct { - Code uint8 - Message string -} - -func (e *Error) Error() string { - return fmt.Sprintf("PCP error: %s (%d)", e.Message, e.Code) -} diff --git a/client/internal/portforward/pcp/client_test.go b/client/internal/portforward/pcp/client_test.go deleted file mode 100644 index 79f44a426..000000000 --- a/client/internal/portforward/pcp/client_test.go +++ /dev/null @@ -1,187 +0,0 @@ -package pcp - -import ( - "context" - "net" - "net/netip" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestAddrConversion(t *testing.T) { - tests := []struct { - name string - addr netip.Addr - }{ - {"IPv4", netip.MustParseAddr("192.168.1.100")}, - {"IPv4 loopback", netip.MustParseAddr("127.0.0.1")}, - {"IPv6", netip.MustParseAddr("2001:db8::1")}, - {"IPv6 loopback", netip.MustParseAddr("::1")}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - b16 := addrTo16(tt.addr) - - recovered := addrFrom16(b16) - assert.Equal(t, tt.addr, recovered, "address should round-trip") - }) - } -} - -func TestBuildAnnounceRequest(t *testing.T) { - clientIP := netip.MustParseAddr("192.168.1.100") - req := buildAnnounceRequest(clientIP) - - require.Len(t, req, headerSize) - assert.Equal(t, byte(Version), req[0], "version") - assert.Equal(t, byte(OpAnnounce), req[1], "opcode") - - // Check client IP is properly encoded as IPv4-mapped IPv6 - assert.Equal(t, byte(0xff), req[18], "IPv4-mapped prefix byte 10") - assert.Equal(t, byte(0xff), req[19], "IPv4-mapped prefix byte 11") - assert.Equal(t, byte(192), req[20], "IP octet 1") - assert.Equal(t, byte(168), req[21], "IP octet 2") - assert.Equal(t, byte(1), req[22], "IP octet 3") - assert.Equal(t, byte(100), req[23], "IP octet 4") -} - -func TestBuildMapRequest(t *testing.T) { - clientIP := netip.MustParseAddr("192.168.1.100") - nonce := [12]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12} - req := buildMapRequest(clientIP, nonce, ProtoUDP, 51820, 51820, netip.Addr{}, 3600) - - require.Len(t, req, mapRequestSize) - assert.Equal(t, byte(Version), req[0], "version") - assert.Equal(t, byte(OpMap), req[1], "opcode") - - // Lifetime at bytes 4-7 - assert.Equal(t, uint32(3600), (uint32(req[4])<<24)|(uint32(req[5])<<16)|(uint32(req[6])<<8)|uint32(req[7]), "lifetime") - - // Nonce at bytes 24-35 - assert.Equal(t, nonce[:], req[24:36], "nonce") - - // Protocol at byte 36 - assert.Equal(t, byte(ProtoUDP), req[36], "protocol") - - // Internal port at bytes 40-41 - assert.Equal(t, uint16(51820), (uint16(req[40])<<8)|uint16(req[41]), "internal port") - - // External port at bytes 42-43 - assert.Equal(t, uint16(51820), (uint16(req[42])<<8)|uint16(req[43]), "external port") -} - -func TestParseResponse(t *testing.T) { - // Construct a valid ANNOUNCE response - resp := make([]byte, headerSize) - resp[0] = Version - resp[1] = OpAnnounce | OpReply - // Result code = 0 (success) - // Lifetime = 0 - // Epoch = 12345 - resp[8] = 0 - resp[9] = 0 - resp[10] = 0x30 - resp[11] = 0x39 - - parsed, err := parseResponse(resp) - require.NoError(t, err) - assert.Equal(t, uint8(Version), parsed.Version) - assert.Equal(t, uint8(OpAnnounce|OpReply), parsed.Opcode) - assert.Equal(t, uint8(ResultSuccess), parsed.ResultCode) - assert.Equal(t, uint32(12345), parsed.Epoch) -} - -func TestParseResponseErrors(t *testing.T) { - t.Run("too short", func(t *testing.T) { - _, err := parseResponse([]byte{1, 2, 3}) - assert.Error(t, err) - }) - - t.Run("wrong version", func(t *testing.T) { - resp := make([]byte, headerSize) - resp[0] = 1 // Wrong version - resp[1] = OpReply - _, err := parseResponse(resp) - assert.Error(t, err) - }) - - t.Run("missing reply bit", func(t *testing.T) { - resp := make([]byte, headerSize) - resp[0] = Version - resp[1] = OpAnnounce // Missing OpReply bit - _, err := parseResponse(resp) - assert.Error(t, err) - }) -} - -func TestResultCodeString(t *testing.T) { - assert.Equal(t, "SUCCESS", ResultCodeString(ResultSuccess)) - assert.Equal(t, "NOT_AUTHORIZED", ResultCodeString(ResultNotAuthorized)) - assert.Equal(t, "ADDRESS_MISMATCH", ResultCodeString(ResultAddressMismatch)) - assert.Contains(t, ResultCodeString(255), "UNKNOWN") -} - -func TestProtocolNumber(t *testing.T) { - proto, err := protocolNumber("udp") - require.NoError(t, err) - assert.Equal(t, uint8(ProtoUDP), proto) - - proto, err = protocolNumber("tcp") - require.NoError(t, err) - assert.Equal(t, uint8(ProtoTCP), proto) - - proto, err = protocolNumber("UDP") - require.NoError(t, err) - assert.Equal(t, uint8(ProtoUDP), proto) - - _, err = protocolNumber("icmp") - assert.Error(t, err) -} - -func TestClientCreation(t *testing.T) { - gateway := netip.MustParseAddr("192.168.1.1").AsSlice() - - client := NewClient(gateway) - assert.Equal(t, net.IP(gateway), client.Gateway()) - assert.Equal(t, defaultTimeout, client.timeout) - - clientWithTimeout := NewClientWithTimeout(gateway, 5*time.Second) - assert.Equal(t, 5*time.Second, clientWithTimeout.timeout) -} - -func TestNATType(t *testing.T) { - n := NewNAT(netip.MustParseAddr("192.168.1.1").AsSlice(), netip.MustParseAddr("192.168.1.100").AsSlice()) - assert.Equal(t, "PCP", n.Type()) -} - -// Integration test - skipped unless PCP_TEST_GATEWAY env is set -func TestClientIntegration(t *testing.T) { - t.Skip("Integration test - run manually with PCP_TEST_GATEWAY=") - - gateway := netip.MustParseAddr("10.0.1.1").AsSlice() // Change to your test gateway - localIP := netip.MustParseAddr("10.0.1.100").AsSlice() // Change to your local IP - - client := NewClient(gateway) - client.SetLocalIP(localIP) - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - // Test ANNOUNCE - epoch, err := client.Announce(ctx) - require.NoError(t, err) - t.Logf("Server epoch: %d", epoch) - - // Test MAP - resp, err := client.AddPortMapping(ctx, "udp", 51820, 1*time.Hour) - require.NoError(t, err) - t.Logf("Mapping: internal=%d external=%d externalIP=%s", - resp.InternalPort, resp.ExternalPort, resp.ExternalIP) - - // Cleanup - err = client.DeletePortMapping(ctx, "udp", 51820) - require.NoError(t, err) -} diff --git a/client/internal/portforward/pcp/nat.go b/client/internal/portforward/pcp/nat.go deleted file mode 100644 index 0e635b6c8..000000000 --- a/client/internal/portforward/pcp/nat.go +++ /dev/null @@ -1,222 +0,0 @@ -package pcp - -import ( - "context" - "fmt" - "net" - "net/netip" - "runtime" - "sync" - "time" - - log "github.com/sirupsen/logrus" - - "github.com/libp2p/go-nat" - "github.com/libp2p/go-netroute" -) - -var _ nat.NAT = (*NAT)(nil) - -// NAT implements the go-nat NAT interface using PCP. -// Supports dual-stack (IPv4 and IPv6) when available. -// All methods are safe for concurrent use. -// -// TODO: IPv6 pinholes use the local IPv6 address. If the address changes -// (e.g., due to SLAAC rotation or network change), the pinhole becomes stale -// and needs to be recreated with the new address. -type NAT struct { - client *Client - - mu sync.RWMutex - // client6 is the IPv6 PCP client, nil if IPv6 is unavailable. - client6 *Client - // localIP6 caches the local IPv6 address used for PCP requests. - localIP6 netip.Addr -} - -// NewNAT creates a new NAT instance backed by PCP. -func NewNAT(gateway, localIP net.IP) *NAT { - client := NewClient(gateway) - client.SetLocalIP(localIP) - return &NAT{ - client: client, - } -} - -// Type returns "PCP" as the NAT type. -func (n *NAT) Type() string { - return "PCP" -} - -// GetDeviceAddress returns the gateway IP address. -func (n *NAT) GetDeviceAddress() (net.IP, error) { - return n.client.Gateway(), nil -} - -// GetExternalAddress returns the external IP address. -func (n *NAT) GetExternalAddress() (net.IP, error) { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - return n.client.GetExternalAddress(ctx) -} - -// GetInternalAddress returns the local IP address used to communicate with the gateway. -func (n *NAT) GetInternalAddress() (net.IP, error) { - addr, err := n.client.getLocalIP() - if err != nil { - return nil, err - } - return addr.AsSlice(), nil -} - -// AddPortMapping creates a port mapping on both IPv4 and IPv6 (if available). -func (n *NAT) AddPortMapping(ctx context.Context, protocol string, internalPort int, _ string, timeout time.Duration) (int, error) { - resp, err := n.client.AddPortMapping(ctx, protocol, internalPort, timeout) - if err != nil { - return 0, fmt.Errorf("add mapping: %w", err) - } - - n.mu.RLock() - client6 := n.client6 - localIP6 := n.localIP6 - n.mu.RUnlock() - - if client6 == nil { - return int(resp.ExternalPort), nil - } - - if _, err := client6.AddPortMapping(ctx, protocol, internalPort, timeout); err != nil { - log.Warnf("IPv6 PCP mapping failed (continuing with IPv4): %v", err) - return int(resp.ExternalPort), nil - } - - log.Infof("created IPv6 PCP pinhole: %s:%d", localIP6, internalPort) - return int(resp.ExternalPort), nil -} - -// DeletePortMapping removes a port mapping from both IPv4 and IPv6. -func (n *NAT) DeletePortMapping(ctx context.Context, protocol string, internalPort int) error { - err := n.client.DeletePortMapping(ctx, protocol, internalPort) - - n.mu.RLock() - client6 := n.client6 - n.mu.RUnlock() - - if client6 != nil { - if err6 := client6.DeletePortMapping(ctx, protocol, internalPort); err6 != nil { - log.Warnf("IPv6 PCP delete mapping failed: %v", err6) - } - } - - if err != nil { - return fmt.Errorf("delete mapping: %w", err) - } - return nil -} - -// CheckServerHealth sends an ANNOUNCE to verify the server is still responsive. -// Returns the current epoch and whether the server may have restarted (epoch state loss detected). -func (n *NAT) CheckServerHealth(ctx context.Context) (epoch uint32, serverRestarted bool, err error) { - epoch, err = n.client.Announce(ctx) - if err != nil { - return 0, false, fmt.Errorf("announce: %w", err) - } - return epoch, n.client.EpochStateLost(), nil -} - -// DiscoverPCP attempts to discover a PCP-capable gateway. -// Returns a NAT interface if PCP is supported, or an error otherwise. -// Discovers both IPv4 and IPv6 gateways when available. -func DiscoverPCP(ctx context.Context) (nat.NAT, error) { - gateway, localIP, err := getDefaultGateway() - if err != nil { - return nil, fmt.Errorf("get default gateway: %w", err) - } - - client := NewClient(gateway) - client.SetLocalIP(localIP) - if _, err := client.Announce(ctx); err != nil { - return nil, fmt.Errorf("PCP announce: %w", err) - } - - result := &NAT{client: client} - discoverIPv6(ctx, result) - - return result, nil -} - -func discoverIPv6(ctx context.Context, result *NAT) { - gateway6, localIP6, err := getDefaultGateway6() - if err != nil { - log.Debugf("IPv6 gateway discovery failed: %v", err) - return - } - - client6 := NewClient(gateway6) - client6.SetLocalIP(localIP6) - if _, err := client6.Announce(ctx); err != nil { - log.Debugf("PCP IPv6 announce failed: %v", err) - return - } - - addr, ok := netip.AddrFromSlice(localIP6) - if !ok { - log.Debugf("invalid IPv6 local IP: %v", localIP6) - return - } - result.mu.Lock() - result.client6 = client6 - result.localIP6 = addr - result.mu.Unlock() - log.Debugf("PCP IPv6 gateway discovered: %s (local: %s)", gateway6, localIP6) -} - -// getDefaultGateway returns the default IPv4 gateway and local IP using the system routing table. -func getDefaultGateway() (gateway net.IP, localIP net.IP, err error) { - router, err := netroute.New() - if err != nil { - return nil, nil, err - } - - dst := net.IPv4zero - if runtime.GOOS == "linux" || runtime.GOOS == "android" { - // go-netroute v0.4.0 rejects unspecified destinations client-side on Linux/Android. - // TODO: on android/ios, use platform APIs (ConnectivityManager.getLinkProperties / - // NWPathMonitor) when netlink-based lookup is restricted or unavailable. - dst = net.IPv4(0, 0, 0, 1) - } - _, gateway, localIP, err = router.Route(dst) - if err != nil { - return nil, nil, err - } - - if gateway == nil { - return nil, nil, nat.ErrNoNATFound - } - - return gateway, localIP, nil -} - -// getDefaultGateway6 returns the default IPv6 gateway IP address using the system routing table. -func getDefaultGateway6() (gateway net.IP, localIP net.IP, err error) { - router, err := netroute.New() - if err != nil { - return nil, nil, err - } - - dst := net.IPv6zero - if runtime.GOOS == "linux" || runtime.GOOS == "android" { - // ::2 - dst = net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2} - } - _, gateway, localIP, err = router.Route(dst) - if err != nil { - return nil, nil, err - } - - if gateway == nil { - return nil, nil, nat.ErrNoNATFound - } - - return gateway, localIP, nil -} diff --git a/client/internal/portforward/pcp/protocol.go b/client/internal/portforward/pcp/protocol.go deleted file mode 100644 index d81c50c8c..000000000 --- a/client/internal/portforward/pcp/protocol.go +++ /dev/null @@ -1,225 +0,0 @@ -// Package pcp implements the Port Control Protocol (RFC 6887). -// -// # Implemented Features -// -// - ANNOUNCE opcode: Discovers PCP server support -// - MAP opcode: Creates/deletes port mappings (IPv4 NAT) and firewall pinholes (IPv6) -// - Dual-stack: Simultaneous IPv4 and IPv6 support via separate clients -// - Nonce validation: Prevents response spoofing -// - Epoch tracking: Detects server restarts per Section 8.5 -// - RFC-compliant retry timing: 3s initial, exponential backoff to 1024s max (Section 8.1.1) -// -// # Not Implemented -// -// - PEER opcode: For outbound peer connections (not needed for inbound NAT traversal) -// - THIRD_PARTY option: For managing mappings on behalf of other devices -// - PREFER_FAILURE option: Requires exact external port or fail (IPv4 NAT only, not needed for IPv6 pinholing) -// - FILTER option: To restrict remote peer addresses -// -// These optional features are omitted because the primary use case is simple -// port forwarding for WireGuard, which only requires MAP with default behavior. -package pcp - -import ( - "encoding/binary" - "fmt" - "net/netip" -) - -const ( - // Version is the PCP protocol version (RFC 6887). - Version = 2 - - // Port is the standard PCP server port. - Port = 5351 - - // DefaultLifetime is the default requested mapping lifetime in seconds. - DefaultLifetime = 7200 // 2 hours - - // Header sizes - headerSize = 24 - mapPayloadSize = 36 - mapRequestSize = headerSize + mapPayloadSize // 60 bytes -) - -// Opcodes -const ( - OpAnnounce = 0 - OpMap = 1 - OpPeer = 2 - OpReply = 0x80 // OR'd with opcode in responses -) - -// Protocol numbers for MAP requests -const ( - ProtoUDP = 17 - ProtoTCP = 6 -) - -// Result codes (RFC 6887 Section 7.4) -const ( - ResultSuccess = 0 - ResultUnsuppVersion = 1 - ResultNotAuthorized = 2 - ResultMalformedRequest = 3 - ResultUnsuppOpcode = 4 - ResultUnsuppOption = 5 - ResultMalformedOption = 6 - ResultNetworkFailure = 7 - ResultNoResources = 8 - ResultUnsuppProtocol = 9 - ResultUserExQuota = 10 - ResultCannotProvideExt = 11 - ResultAddressMismatch = 12 - ResultExcessiveRemotePeers = 13 -) - -// ResultCodeString returns a human-readable string for a result code. -func ResultCodeString(code uint8) string { - switch code { - case ResultSuccess: - return "SUCCESS" - case ResultUnsuppVersion: - return "UNSUPP_VERSION" - case ResultNotAuthorized: - return "NOT_AUTHORIZED" - case ResultMalformedRequest: - return "MALFORMED_REQUEST" - case ResultUnsuppOpcode: - return "UNSUPP_OPCODE" - case ResultUnsuppOption: - return "UNSUPP_OPTION" - case ResultMalformedOption: - return "MALFORMED_OPTION" - case ResultNetworkFailure: - return "NETWORK_FAILURE" - case ResultNoResources: - return "NO_RESOURCES" - case ResultUnsuppProtocol: - return "UNSUPP_PROTOCOL" - case ResultUserExQuota: - return "USER_EX_QUOTA" - case ResultCannotProvideExt: - return "CANNOT_PROVIDE_EXTERNAL" - case ResultAddressMismatch: - return "ADDRESS_MISMATCH" - case ResultExcessiveRemotePeers: - return "EXCESSIVE_REMOTE_PEERS" - default: - return fmt.Sprintf("UNKNOWN(%d)", code) - } -} - -// Response represents a parsed PCP response header. -type Response struct { - Version uint8 - Opcode uint8 - ResultCode uint8 - Lifetime uint32 - Epoch uint32 -} - -// MapResponse contains the full response to a MAP request. -type MapResponse struct { - Response - Nonce [12]byte - Protocol uint8 - InternalPort uint16 - ExternalPort uint16 - ExternalIP netip.Addr -} - -// addrTo16 converts an address to its 16-byte IPv4-mapped IPv6 representation. -func addrTo16(addr netip.Addr) [16]byte { - if addr.Is4() { - return netip.AddrFrom4(addr.As4()).As16() - } - return addr.As16() -} - -// addrFrom16 extracts an address from a 16-byte representation, unmapping IPv4. -func addrFrom16(b [16]byte) netip.Addr { - return netip.AddrFrom16(b).Unmap() -} - -// buildAnnounceRequest creates a PCP ANNOUNCE request packet. -func buildAnnounceRequest(clientIP netip.Addr) []byte { - req := make([]byte, headerSize) - req[0] = Version - req[1] = OpAnnounce - mapped := addrTo16(clientIP) - copy(req[8:24], mapped[:]) - return req -} - -// buildMapRequest creates a PCP MAP request packet. -func buildMapRequest(clientIP netip.Addr, nonce [12]byte, protocol uint8, internalPort, suggestedExtPort uint16, suggestedExtIP netip.Addr, lifetime uint32) []byte { - req := make([]byte, mapRequestSize) - - // Header - req[0] = Version - req[1] = OpMap - binary.BigEndian.PutUint32(req[4:8], lifetime) - mapped := addrTo16(clientIP) - copy(req[8:24], mapped[:]) - - // MAP payload - copy(req[24:36], nonce[:]) - req[36] = protocol - binary.BigEndian.PutUint16(req[40:42], internalPort) - binary.BigEndian.PutUint16(req[42:44], suggestedExtPort) - if suggestedExtIP.IsValid() { - extMapped := addrTo16(suggestedExtIP) - copy(req[44:60], extMapped[:]) - } - - return req -} - -// parseResponse parses the common PCP response header. -func parseResponse(data []byte) (*Response, error) { - if len(data) < headerSize { - return nil, fmt.Errorf("response too short: %d bytes", len(data)) - } - - resp := &Response{ - Version: data[0], - Opcode: data[1], - ResultCode: data[3], // Byte 2 is reserved, byte 3 is result code (RFC 6887 §7.2) - Lifetime: binary.BigEndian.Uint32(data[4:8]), - Epoch: binary.BigEndian.Uint32(data[8:12]), - } - - if resp.Version != Version { - return nil, fmt.Errorf("unsupported PCP version: %d", resp.Version) - } - - if resp.Opcode&OpReply == 0 { - return nil, fmt.Errorf("response missing reply bit: opcode=0x%02x", resp.Opcode) - } - - return resp, nil -} - -// parseMapResponse parses a complete MAP response. -func parseMapResponse(data []byte) (*MapResponse, error) { - if len(data) < mapRequestSize { - return nil, fmt.Errorf("MAP response too short: %d bytes", len(data)) - } - - resp, err := parseResponse(data) - if err != nil { - return nil, fmt.Errorf("parse header: %w", err) - } - - mapResp := &MapResponse{ - Response: *resp, - Protocol: data[36], - InternalPort: binary.BigEndian.Uint16(data[40:42]), - ExternalPort: binary.BigEndian.Uint16(data[42:44]), - ExternalIP: addrFrom16([16]byte(data[44:60])), - } - copy(mapResp.Nonce[:], data[24:36]) - - return mapResp, nil -} diff --git a/client/internal/portforward/pinhole_test.go b/client/internal/portforward/pinhole_test.go new file mode 100644 index 000000000..46b07a9e7 --- /dev/null +++ b/client/internal/portforward/pinhole_test.go @@ -0,0 +1,116 @@ +//go:build !js + +package portforward + +import ( + "context" + "errors" + "strings" + "testing" + + "github.com/netbirdio/go-nat" + log "github.com/sirupsen/logrus" + "github.com/sirupsen/logrus/hooks/test" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// mockPinholeNAT is a gateway that also reports an IPv6 pinhole outcome, the +// shape a dual-stack gateway has. +type mockPinholeNAT struct { + *mockNAT + pinholeErr error +} + +func (m *mockPinholeNAT) IPv6PinholeError() error { + return m.pinholeErr +} + +func TestSetupLogsPinholeOutcome(t *testing.T) { + pinholeErr := errors.New("pcp ipv6: NOT_AUTHORIZED") + + tests := []struct { + name string + pinholeErr error + mappingErr error + wantLevel log.Level + wantText string + }{ + { + name: "an open pinhole is reported", + wantLevel: log.InfoLevel, + wantText: "IPv6 pinhole open", + }, + { + name: "a failed pinhole is reported without failing the mapping", + // The IPv4 mapping is what the caller asked for, so the pinhole + // failure surfaces only in the log. + pinholeErr: pinholeErr, + wantLevel: log.WarnLevel, + wantText: pinholeErr.Error(), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gateway := &mockPinholeNAT{mockNAT: newMockNAT(), pinholeErr: tt.pinholeErr} + hook := stubGatewayDiscovery(t, gateway) + + m := NewManager() + m.wgPort = 51820 + + _, mapping, err := m.setup(context.Background()) + + require.NoError(t, err) + require.NotNil(t, mapping) + + entry := findEntry(hook, tt.wantText) + require.NotNil(t, entry, "no log entry mentioning %q", tt.wantText) + assert.Equal(t, tt.wantLevel, entry.Level) + }) + } + + t.Run("a failed mapping reports no pinhole outcome", func(t *testing.T) { + // Nothing opened the pinhole, so whatever it currently reports says + // nothing about this attempt. + gateway := &mockPinholeNAT{mockNAT: newMockNAT()} + gateway.addMappingErr = errors.New("gateway refused") + hook := stubGatewayDiscovery(t, gateway) + + m := NewManager() + m.wgPort = 51820 + + _, _, err := m.setup(context.Background()) + + require.Error(t, err) + assert.Nil(t, findEntry(hook, "IPv6 pinhole")) + }) +} + +// stubGatewayDiscovery makes discovery return gateway and captures log output. +func stubGatewayDiscovery(t *testing.T, gateway nat.NAT) *test.Hook { + t.Helper() + + orig := discoverGateway + discoverGateway = func(context.Context) (nat.NAT, error) { return gateway, nil } + t.Cleanup(func() { discoverGateway = orig }) + + hook := test.NewGlobal() + origLevel := log.GetLevel() + log.SetLevel(log.DebugLevel) + t.Cleanup(func() { + hook.Reset() + log.SetLevel(origLevel) + }) + + return hook +} + +func findEntry(hook *test.Hook, substr string) *log.Entry { + for _, entry := range hook.AllEntries() { + if strings.Contains(entry.Message, substr) { + return entry + } + } + return nil +} diff --git a/client/internal/portforward/state.go b/client/internal/portforward/state.go index b1315cdc0..a21368e58 100644 --- a/client/internal/portforward/state.go +++ b/client/internal/portforward/state.go @@ -4,27 +4,94 @@ package portforward import ( "context" + "errors" "fmt" + "time" - "github.com/libp2p/go-nat" + "github.com/netbirdio/go-nat" + "github.com/netbirdio/go-nat/pcp" log "github.com/sirupsen/logrus" - - "github.com/netbirdio/netbird/client/internal/portforward/pcp" ) // discoverGateway is the function used for NAT gateway discovery. // It can be replaced in tests to avoid real network operations. -// Tries PCP first, then falls back to NAT-PMP/UPnP. var discoverGateway = defaultDiscoverGateway -func defaultDiscoverGateway(ctx context.Context) (nat.NAT, error) { - pcpGateway, err := pcp.DiscoverPCP(ctx) - if err == nil { - return pcpGateway, nil - } - log.Debugf("PCP discovery failed: %v, trying NAT-PMP/UPnP", err) +// pinholeDiscoveryTimeout is the slice of the discovery budget held back for +// the IPv6 pinhole probe. +// +// Sizing it is coarser than it looks: PCP retransmits on a 3s socket timeout +// and a 3s first backoff, so a second attempt needs about 9s. Anything from +// roughly 1s to 8s therefore buys exactly one attempt, and this only sets how +// long that attempt waits. A PCP server sits on the local link and answers in +// milliseconds, so 3s is margin rather than need, and the rest is left to +// gateway discovery, whose multicast SSDP search alone takes 5s. A probe lost +// to a dropped packet is retried by the next discovery round. +// +// It is a variable so tests can shorten it. +var pinholeDiscoveryTimeout = 3 * time.Second - return nat.DiscoverGateway(ctx) +// Discovery entry points, as variables so tests can drive the fallback without +// touching the network. +var ( + discoverNATGateway = nat.DiscoverGateway + + discoverPCPPinhole = func(ctx context.Context) (nat.NAT, error) { + pinhole, err := pcp.DiscoverPCP(ctx) + if err != nil { + return nil, err + } + return pinhole, nil + } +) + +// defaultDiscoverGateway finds a gateway that can make the WireGuard port +// reachable. DiscoverGateway prefers PCP for IPv4, races UPnP and NAT-PMP +// behind it, and attaches an IPv6 pinhole independently of which IPv4 protocol +// wins. +// +// It reports no gateway on a network offering only IPv6, having no IPv4 mapping +// to attach a pinhole to. Such a network still needs one: there is no +// translation to traverse, but the router drops inbound IPv6 until something +// opens it. Fall back to PCP alone, which yields a gateway holding just the +// pinhole. +func defaultDiscoverGateway(ctx context.Context) (nat.NAT, error) { + gatewayCtx, cancel := reserveForPinhole(ctx) + defer cancel() + + gateway, err := discoverNATGateway(gatewayCtx) + if err == nil { + return gateway, nil + } + if !errors.Is(err, nat.ErrNoNATFound) { + return nil, err + } + + pinhole, pinholeErr := discoverPCPPinhole(ctx) + if pinholeErr != nil { + log.Debugf("no IPv6 pinhole after %v: %v", err, pinholeErr) + return nil, err + } + + log.Infof("no IPv4 gateway, continuing with an IPv6 pinhole only") + return pinhole, nil +} + +// reserveForPinhole shortens ctx so that a pinhole probe still has time to run +// afterwards. Finding nothing takes gateway discovery everything it is given, +// so on the unshortened context the probe would start already expired. A budget +// too small to divide is left to gateway discovery, which is the likelier win. +func reserveForPinhole(ctx context.Context) (context.Context, context.CancelFunc) { + deadline, ok := ctx.Deadline() + if !ok { + return context.WithCancel(ctx) + } + + remaining := time.Until(deadline) + if remaining <= pinholeDiscoveryTimeout { + return context.WithCancel(ctx) + } + return context.WithTimeout(ctx, remaining-pinholeDiscoveryTimeout) } // State is persisted only for crash recovery cleanup diff --git a/client/internal/portforward/state_test.go b/client/internal/portforward/state_test.go new file mode 100644 index 000000000..8a584eecb --- /dev/null +++ b/client/internal/portforward/state_test.go @@ -0,0 +1,140 @@ +//go:build !js + +package portforward + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/netbirdio/go-nat" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// stubDiscovery replaces both discovery entry points for the duration of a +// test. gatewayDelay simulates gateway discovery spending everything it is +// given before reporting that it found nothing. +func stubDiscovery(t *testing.T, gateway nat.NAT, gatewayErr error, gatewayDelay time.Duration, pinhole nat.NAT, pinholeErr error) { + t.Helper() + + origGateway, origPinhole := discoverNATGateway, discoverPCPPinhole + discoverNATGateway = func(ctx context.Context) (nat.NAT, error) { + if gatewayDelay > 0 { + select { + case <-time.After(gatewayDelay): + case <-ctx.Done(): + } + } + return gateway, gatewayErr + } + discoverPCPPinhole = func(ctx context.Context) (nat.NAT, error) { + if err := ctx.Err(); err != nil { + return nil, err + } + return pinhole, pinholeErr + } + + t.Cleanup(func() { discoverNATGateway, discoverPCPPinhole = origGateway, origPinhole }) +} + +func TestDefaultDiscoverGateway(t *testing.T) { + ipv4Gateway := &mockNAT{natType: "PCP+PCPv6"} + ipv6Pinhole := &mockNAT{natType: "PCP"} + otherErr := errors.New("routing table unavailable") + + t.Run("an IPv4 gateway is used as is", func(t *testing.T) { + stubDiscovery(t, ipv4Gateway, nil, 0, ipv6Pinhole, nil) + + got, err := defaultDiscoverGateway(context.Background()) + + require.NoError(t, err) + assert.Same(t, ipv4Gateway, got) + }) + + t.Run("no IPv4 gateway still opens an IPv6 pinhole", func(t *testing.T) { + stubDiscovery(t, nil, nat.ErrNoNATFound, 0, ipv6Pinhole, nil) + + got, err := defaultDiscoverGateway(context.Background()) + + require.NoError(t, err) + assert.Same(t, ipv6Pinhole, got) + }) + + t.Run("no gateway and no pinhole reports the original failure", func(t *testing.T) { + stubDiscovery(t, nil, nat.ErrNoNATFound, 0, nil, errors.New("no IPv6 route")) + + got, err := defaultDiscoverGateway(context.Background()) + + assert.Nil(t, got) + assert.ErrorIs(t, err, nat.ErrNoNATFound, "the pinhole failure must not mask why no gateway was found") + }) + + t.Run("a failure other than no-gateway is reported as is", func(t *testing.T) { + stubDiscovery(t, nil, otherErr, 0, ipv6Pinhole, nil) + + got, err := defaultDiscoverGateway(context.Background()) + + assert.Nil(t, got) + assert.ErrorIs(t, err, otherErr) + }) + + t.Run("the pinhole survives gateway discovery using its whole budget", func(t *testing.T) { + // On one shared context the probe would start already expired, which is + // how this failed against a real gateway. + reserve := 50 * time.Millisecond + origReserve := pinholeDiscoveryTimeout + pinholeDiscoveryTimeout = reserve + t.Cleanup(func() { pinholeDiscoveryTimeout = origReserve }) + + budget := 4 * reserve + ctx, cancel := context.WithTimeout(context.Background(), budget) + defer cancel() + + stubDiscovery(t, nil, nat.ErrNoNATFound, budget, ipv6Pinhole, nil) + + got, err := defaultDiscoverGateway(ctx) + + require.NoError(t, err) + assert.Same(t, ipv6Pinhole, got) + }) +} + +func TestReserveForPinhole(t *testing.T) { + origReserve := pinholeDiscoveryTimeout + pinholeDiscoveryTimeout = time.Second + t.Cleanup(func() { pinholeDiscoveryTimeout = origReserve }) + + t.Run("a budget is divided", func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + gatewayCtx, cancelGateway := reserveForPinhole(ctx) + defer cancelGateway() + + deadline, ok := gatewayCtx.Deadline() + require.True(t, ok) + assert.InDelta(t, 9*time.Second, time.Until(deadline), float64(500*time.Millisecond)) + }) + + t.Run("a budget too small to divide is left whole", func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) + defer cancel() + + gatewayCtx, cancelGateway := reserveForPinhole(ctx) + defer cancelGateway() + + deadline, ok := gatewayCtx.Deadline() + require.True(t, ok) + assert.InDelta(t, 500*time.Millisecond, time.Until(deadline), float64(100*time.Millisecond)) + }) + + t.Run("no deadline stays unbounded", func(t *testing.T) { + gatewayCtx, cancelGateway := reserveForPinhole(context.Background()) + defer cancelGateway() + + _, ok := gatewayCtx.Deadline() + assert.False(t, ok) + }) +} diff --git a/client/internal/profilemanager/prefs.go b/client/internal/profilemanager/prefs.go new file mode 100644 index 000000000..5613b0be3 --- /dev/null +++ b/client/internal/profilemanager/prefs.go @@ -0,0 +1,130 @@ +package profilemanager + +import ( + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "sync" + + "github.com/netbirdio/netbird/util" +) + +const prefsFileSuffix = ".prefs.json" + +var prefsMu sync.Mutex + +// Prefs is a namespaced per-profile preference store backed by a single JSON +// file next to the profile config; it is deleted together with the profile. +type Prefs struct { + path string +} + +// ProfilePrefs returns the preference store of the profile identified by id. +func (s *ServiceManager) ProfilePrefs(id ID, username string) (*Prefs, error) { + if !IsValidProfileFilenameStem(id) { + return nil, fmt.Errorf("invalid profile ID: %q", id) + } + if id == defaultProfileName { + return &Prefs{path: filepath.Join(filepath.Dir(DefaultConfigPath), id.String()+prefsFileSuffix)}, nil + } + configDir, err := s.getConfigDir(username) + if err != nil { + return nil, fmt.Errorf("get config directory for user %s: %w", username, err) + } + return &Prefs{path: filepath.Join(configDir, id.String()+prefsFileSuffix)}, nil +} + +// Get unmarshals the namespace section into v and reports whether it exists. +func (p *Prefs) Get(namespace string, v any) (bool, error) { + if namespace == "" { + return false, fmt.Errorf("empty prefs namespace") + } + + prefsMu.Lock() + defer prefsMu.Unlock() + + sections, err := readPrefsFile(p.path) + if err != nil { + return false, err + } + raw, ok := sections[namespace] + if !ok { + return false, nil + } + if err := json.Unmarshal(raw, v); err != nil { + return false, fmt.Errorf("decode prefs namespace %q: %w", namespace, err) + } + return true, nil +} + +// Put stores v as the namespace section, replacing any previous value. +func (p *Prefs) Put(namespace string, v any) error { + if namespace == "" { + return fmt.Errorf("empty prefs namespace") + } + raw, err := json.Marshal(v) + if err != nil { + return fmt.Errorf("encode prefs namespace %q: %w", namespace, err) + } + + prefsMu.Lock() + defer prefsMu.Unlock() + + sections, err := readPrefsFile(p.path) + if err != nil { + return err + } + sections[namespace] = raw + return writePrefsFile(p.path, sections) +} + +// Remove deletes the namespace section; a missing one is not an error. +func (p *Prefs) Remove(namespace string) error { + if namespace == "" { + return fmt.Errorf("empty prefs namespace") + } + + prefsMu.Lock() + defer prefsMu.Unlock() + + sections, err := readPrefsFile(p.path) + if err != nil { + return err + } + if _, ok := sections[namespace]; !ok { + return nil + } + delete(sections, namespace) + return writePrefsFile(p.path, sections) +} + +func removePrefsFile(path string) error { + prefsMu.Lock() + defer prefsMu.Unlock() + return os.Remove(path) +} + +func readPrefsFile(path string) (map[string]json.RawMessage, error) { + data, err := os.ReadFile(path) + if os.IsNotExist(err) { + return map[string]json.RawMessage{}, nil + } + if err != nil { + return nil, fmt.Errorf("read prefs: %w", err) + } + + sections := map[string]json.RawMessage{} + if err := json.Unmarshal(data, §ions); err != nil { + return nil, fmt.Errorf("decode prefs: %w", err) + } + return sections, nil +} + +func writePrefsFile(path string, sections map[string]json.RawMessage) error { + if err := util.WriteJsonWithRestrictedPermission(context.Background(), path, sections); err != nil { + return fmt.Errorf("write prefs: %w", err) + } + return nil +} diff --git a/client/internal/profilemanager/prefs_test.go b/client/internal/profilemanager/prefs_test.go new file mode 100644 index 000000000..692ade70f --- /dev/null +++ b/client/internal/profilemanager/prefs_test.go @@ -0,0 +1,138 @@ +package profilemanager + +import ( + "errors" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testPrefsSection struct { + Mode uint8 `json:"mode"` + Dest string `json:"dest"` +} + +func TestProfilePrefs_RoundTrip(t *testing.T) { + withTestSM(t, func(sm *ServiceManager, username string) { + created, err := sm.AddProfile("work", username) + require.NoError(t, err) + + prefs, err := sm.ProfilePrefs(created.ID, username) + require.NoError(t, err) + + require.NoError(t, prefs.Put("filedrop", testPrefsSection{Mode: 2, Dest: "/tmp/x"})) + require.NoError(t, prefs.Put("other", map[string]int{"n": 1})) + + var got testPrefsSection + found, err := prefs.Get("filedrop", &got) + require.NoError(t, err) + assert.True(t, found) + assert.Equal(t, testPrefsSection{Mode: 2, Dest: "/tmp/x"}, got) + + var other map[string]int + found, err = prefs.Get("other", &other) + require.NoError(t, err) + assert.True(t, found) + assert.Equal(t, map[string]int{"n": 1}, other) + }) +} + +func TestProfilePrefs_GetMissingNamespace(t *testing.T) { + withTestSM(t, func(sm *ServiceManager, username string) { + created, err := sm.AddProfile("work", username) + require.NoError(t, err) + + prefs, err := sm.ProfilePrefs(created.ID, username) + require.NoError(t, err) + + var got testPrefsSection + found, err := prefs.Get("filedrop", &got) + require.NoError(t, err) + assert.False(t, found) + }) +} + +func TestProfilePrefs_RemoveNamespace(t *testing.T) { + withTestSM(t, func(sm *ServiceManager, username string) { + created, err := sm.AddProfile("work", username) + require.NoError(t, err) + + prefs, err := sm.ProfilePrefs(created.ID, username) + require.NoError(t, err) + + require.NoError(t, prefs.Put("filedrop", testPrefsSection{Mode: 1})) + require.NoError(t, prefs.Put("other", map[string]int{"n": 1})) + require.NoError(t, prefs.Remove("filedrop")) + require.NoError(t, prefs.Remove("missing")) + + var got testPrefsSection + found, err := prefs.Get("filedrop", &got) + require.NoError(t, err) + assert.False(t, found) + + var other map[string]int + found, err = prefs.Get("other", &other) + require.NoError(t, err) + assert.True(t, found) + assert.Equal(t, map[string]int{"n": 1}, other) + }) +} + +func TestProfilePrefs_RejectsInvalidID(t *testing.T) { + withTestSM(t, func(sm *ServiceManager, username string) { + _, err := sm.ProfilePrefs("../escape", username) + assert.Error(t, err) + }) +} + +func TestProfilePrefs_RejectsEmptyNamespace(t *testing.T) { + withTestSM(t, func(sm *ServiceManager, username string) { + created, err := sm.AddProfile("work", username) + require.NoError(t, err) + + prefs, err := sm.ProfilePrefs(created.ID, username) + require.NoError(t, err) + + _, err = prefs.Get("", &testPrefsSection{}) + assert.Error(t, err) + assert.Error(t, prefs.Put("", testPrefsSection{})) + assert.Error(t, prefs.Remove("")) + }) +} + +func TestProfilePrefs_DefaultProfile(t *testing.T) { + withTestSM(t, func(sm *ServiceManager, username string) { + prefs, err := sm.ProfilePrefs(defaultProfileName, username) + require.NoError(t, err) + + require.NoError(t, prefs.Put("filedrop", testPrefsSection{Mode: 1})) + + expected := filepath.Join(filepath.Dir(DefaultConfigPath), "default"+prefsFileSuffix) + _, err = os.Stat(expected) + require.NoError(t, err) + }) +} + +func TestRemoveProfile_DeletesPrefsFile(t *testing.T) { + withTestSM(t, func(sm *ServiceManager, username string) { + created, err := sm.AddProfile("work", username) + require.NoError(t, err) + + prefs, err := sm.ProfilePrefs(created.ID, username) + require.NoError(t, err) + require.NoError(t, prefs.Put("filedrop", testPrefsSection{Mode: 2})) + + configDir, err := sm.getConfigDir(username) + require.NoError(t, err) + prefsPath := filepath.Join(configDir, created.ID.String()+prefsFileSuffix) + _, err = os.Stat(prefsPath) + require.NoError(t, err) + + require.NoError(t, sm.RemoveProfile(created.ID, username)) + _, err = os.Stat(prefsPath) + assert.True(t, errors.Is(err, os.ErrNotExist), "prefs file should be removed") + }) +} diff --git a/client/internal/profilemanager/service.go b/client/internal/profilemanager/service.go index 696a60310..ec287f01a 100644 --- a/client/internal/profilemanager/service.go +++ b/client/internal/profilemanager/service.go @@ -420,6 +420,11 @@ func (s *ServiceManager) RemoveProfile(id ID, username string) error { log.Warnf("failed to remove profile state file %s: %v", stateFile, err) } + prefsFile := filepath.Join(filepath.Dir(target.Path), id.String()+prefsFileSuffix) + if err := removePrefsFile(prefsFile); err != nil && !os.IsNotExist(err) { + log.Warnf("failed to remove profile prefs file %s: %v", prefsFile, err) + } + return nil } diff --git a/client/internal/sleep/service.go b/client/internal/sleep/service.go index 196a33f52..93691c4c7 100644 --- a/client/internal/sleep/service.go +++ b/client/internal/sleep/service.go @@ -18,8 +18,8 @@ type Service struct { } func New() (*Service, error) { - d, err := NewDetector() - if err != nil { + d, err := NewDetector() //nolint:staticcheck + if err != nil { //nolint:staticcheck // always errors on platforms without a sleep detector return nil, err } diff --git a/client/internal/updater/installer/doc.go b/client/internal/updater/installer/doc.go index 0a60454bb..11b0512ac 100644 --- a/client/internal/updater/installer/doc.go +++ b/client/internal/updater/installer/doc.go @@ -37,23 +37,32 @@ // Updater Process (Setup): // // 1. Receives parameters from service via command-line arguments -// 2. Runs installer with appropriate silent/quiet flags: +// 2. Terminates the UI so the installer does not have to replace a locked image +// file, which would otherwise leave the install needing a reboot +// 3. Runs installer with appropriate silent/quiet flags: // - Windows EXE: installer.exe /S -// - Windows MSI: msiexec.exe /i installer.msi /quiet /qn /l*v msi.log +// - Windows MSI: msiexec.exe /i installer.msi /qn /norestart REBOOT=ReallySuppress /l*v msi.log // - macOS PKG: installer -pkg installer.pkg -target / // - macOS Homebrew: brew upgrade netbirdio/tap/netbird -// 3. Installer terminates daemon and UI processes -// 4. Installer replaces binaries with new version -// 5. Updater waits for installer to complete -// 6. Updater restarts daemon: +// 4. Installer terminates the daemon +// 5. Installer replaces binaries with new version +// 6. Updater waits for installer to complete. On Windows, MSI exit codes 3010 +// (ERROR_SUCCESS_REBOOT_REQUIRED) and 1641 (ERROR_SUCCESS_REBOOT_INITIATED) +// are a pending-reboot outcome, not a failure: the install succeeded, but +// some files are only replaced on the next restart (the reboot itself is +// suppressed via /norestart and REBOOT=ReallySuppress), and the flow +// continues as on success +// 7. Updater restarts daemon: // - Windows: netbird.exe service start // - macOS/Linux: netbird service start -// 7. Updater restarts UI: -// - Windows: Launches netbird-ui.exe as active console user using CreateProcessAsUser +// 8. Updater restarts UI: +// - Windows: Launches netbird-ui.exe using CreateProcessAsUser in every +// session it was terminated in, falling back to the active console session // - macOS: Uses launchctl asuser to launch NetBird.app for console user // - Linux: Not implemented (UI typically auto-starts) -// 8. Updater writes result.json with success/error status -// 9. Updater process exits +// 9. Updater writes result.json with success/error status (a pending reboot is +// recorded as success) +// 10. Updater process exits // // # Result Communication // diff --git a/client/internal/updater/installer/installer_common.go b/client/internal/updater/installer/installer_common.go index 8e44bee82..17566f7de 100644 --- a/client/internal/updater/installer/installer_common.go +++ b/client/internal/updater/installer/installer_common.go @@ -42,6 +42,9 @@ func NewWithDir(tempDir string) *Installer { // This will run by the original service process func (u *Installer) RunInstallation(ctx context.Context, targetVersion string) (err error) { resultHandler := NewResultHandler(u.tempDir) + if err := resultHandler.ClearStaleResult(); err != nil { + log.Warnf("clear stale installer result: %v", err) + } defer func() { if err != nil { diff --git a/client/internal/updater/installer/installer_run_windows.go b/client/internal/updater/installer/installer_run_windows.go index 70c7e32cf..b2ecf3299 100644 --- a/client/internal/updater/installer/installer_run_windows.go +++ b/client/internal/updater/installer/installer_run_windows.go @@ -2,6 +2,7 @@ package installer import ( "context" + "errors" "fmt" "os" "os/exec" @@ -22,6 +23,12 @@ const ( msiLogFile = "msi.log" + // ERROR_SUCCESS_REBOOT_REQUIRED and ERROR_SUCCESS_REBOOT_INITIATED + msiRebootRequired = 3010 + msiRebootInitiated = 1641 + + processExitWait = 10 * time.Second + msiDownloadURL = "https://github.com/netbirdio/netbird/releases/download/v%version/netbird_installer_%version_windows_%arch.msi" exeDownloadURL = "https://github.com/netbirdio/netbird/releases/download/v%version/netbird_installer_%version_windows_%arch.exe" ) @@ -38,6 +45,8 @@ var ( func (u *Installer) Setup(ctx context.Context, dryRun bool, installerFile string, daemonFolder string) (resultErr error) { resultHandler := NewResultHandler(u.tempDir) + var uiSessions []uint32 + // Always ensure daemon and UI are restarted after setup defer func() { log.Infof("starting daemon back") @@ -46,7 +55,7 @@ func (u *Installer) Setup(ctx context.Context, dryRun bool, installerFile string } log.Infof("starting UI back") - if err := u.startUIAsUser(daemonFolder); err != nil { + if err := u.startUI(daemonFolder, uiSessions); err != nil { log.Errorf("failed to start UI: %v", err) } @@ -75,6 +84,14 @@ func (u *Installer) Setup(ctx context.Context, dryRun bool, installerFile string return } + // The UI holds an open handle on its own image. Left running, Restart Manager + // cannot shut it down (msiexec runs as LocalSystem here, the UI as the + // interactive user), so the MSI falls back to replacing the file on reboot and + // marks the install as restart-required. The deferred close-application action + // in the package runs too late to prevent that, it happens after + // InstallValidate has already registered the file as in use. + uiSessions = killUI() + var cmd *exec.Cmd switch installerType { case TypeExe: @@ -84,7 +101,9 @@ func (u *Installer) Setup(ctx context.Context, dryRun bool, installerFile string installerDir := filepath.Dir(installerFile) logPath := filepath.Join(installerDir, msiLogFile) log.Infof("run msi installer: %s", installerFile) - cmd = exec.CommandContext(ctx, "msiexec.exe", "/i", filepath.Base(installerFile), "/quiet", "/qn", "/l*v", logPath) + // REBOOT=ReallySuppress: a silent install has no way to ask, so without it + // msiexec reboots the machine on its own if it decides one is needed. + cmd = exec.CommandContext(ctx, "msiexec.exe", "/i", filepath.Base(installerFile), "/qn", "/norestart", "REBOOT=ReallySuppress", "/l*v", logPath) } cmd.Dir = filepath.Dir(installerFile) @@ -95,9 +114,13 @@ func (u *Installer) Setup(ctx context.Context, dryRun bool, installerFile string } log.Infof("installer started with PID %d", cmd.Process.Pid) - if resultErr = cmd.Wait(); resultErr != nil { - log.Errorf("installer process finished with error: %v", resultErr) - return + if err := cmd.Wait(); err != nil { + if !isRebootPending(err) { + resultErr = err + log.Errorf("installer process finished with error: %v", err) + return + } + log.Warnf("installer completed but reported a pending reboot, some files will be replaced on the next restart") } return nil @@ -117,16 +140,142 @@ func (u *Installer) startDaemon(daemonFolder string) error { return nil } -func (u *Installer) startUIAsUser(daemonFolder string) error { +func (u *Installer) startUI(daemonFolder string, sessionIDs []uint32) error { uiPath := filepath.Join(daemonFolder, uiName) log.Infof("starting netbird-ui: %s", uiPath) - // Get the active console session ID - sessionID := windows.WTSGetActiveConsoleSessionId() - if sessionID == 0xFFFFFFFF { - return fmt.Errorf("no active user session found") + if len(sessionIDs) == 0 { + sessionID := windows.WTSGetActiveConsoleSessionId() + if sessionID == 0xFFFFFFFF { + return fmt.Errorf("no active user session found") + } + sessionIDs = []uint32{sessionID} } + var errs []error + for _, sessionID := range sessionIDs { + if err := startUIInSession(uiPath, sessionID); err != nil { + errs = append(errs, fmt.Errorf("session %d: %w", sessionID, err)) + continue + } + log.Infof("netbird-ui started successfully in session %d", sessionID) + } + return errors.Join(errs...) +} + +// isRebootPending reports whether the installer exit code means it succeeded but +// left work for the next restart. The reboot itself is suppressed, so this is not +// a failure. +func isRebootPending(err error) bool { + var exitErr *exec.ExitError + if !errors.As(err, &exitErr) { + return false + } + + switch exitErr.ExitCode() { + case msiRebootRequired, msiRebootInitiated: + return true + default: + return false + } +} + +// killUI terminates any running netbird-ui process and returns the IDs of the +// interactive sessions the terminated processes belonged to. Setup starts the +// UI again in those sessions once the installer is done. +func killUI() []uint32 { + pids, err := processIDsByName(uiName) + if err != nil { + log.Warnf("failed to look up %s processes: %v", uiName, err) + return nil + } + + sessions := make(map[uint32]struct{}) + for _, pid := range pids { + var sessionID uint32 + if err := windows.ProcessIdToSessionId(pid, &sessionID); err != nil { + log.Warnf("failed to look up session of %s (PID %d): %v", uiName, pid, err) + } + + if err := terminateProcess(pid); err != nil { + log.Warnf("failed to terminate %s (PID %d): %v", uiName, pid, err) + continue + } + log.Infof("terminated %s (PID %d) in session %d", uiName, pid, sessionID) + + if sessionID != 0 { + sessions[sessionID] = struct{}{} + } + } + + sessionIDs := make([]uint32, 0, len(sessions)) + for sessionID := range sessions { + sessionIDs = append(sessionIDs, sessionID) + } + return sessionIDs +} + +func processIDsByName(name string) ([]uint32, error) { + snapshot, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0) + if err != nil { + return nil, fmt.Errorf("create process snapshot: %w", err) + } + defer func() { + if err := windows.CloseHandle(snapshot); err != nil { + log.Warnf("failed to close process snapshot: %v", err) + } + }() + + var entry windows.ProcessEntry32 + entry.Size = uint32(unsafe.Sizeof(entry)) + + var pids []uint32 + for err = windows.Process32First(snapshot, &entry); err == nil; err = windows.Process32Next(snapshot, &entry) { + if strings.EqualFold(windows.UTF16ToString(entry.ExeFile[:]), name) { + pids = append(pids, entry.ProcessID) + } + } + if !errors.Is(err, windows.ERROR_NO_MORE_FILES) { + return nil, fmt.Errorf("enumerate processes: %w", err) + } + + return pids, nil +} + +func terminateProcess(pid uint32) error { + handle, err := windows.OpenProcess(windows.PROCESS_TERMINATE|windows.SYNCHRONIZE, false, pid) + if err != nil { + // The process may have exited between enumeration and now. + if errors.Is(err, windows.ERROR_INVALID_PARAMETER) { + return nil + } + return fmt.Errorf("open process: %w", err) + } + defer func() { + if err := windows.CloseHandle(handle); err != nil { + log.Warnf("failed to close process handle: %v", err) + } + }() + + if err := windows.TerminateProcess(handle, 0); err != nil { + return fmt.Errorf("terminate process: %w", err) + } + + // Wait for the handle to signal so the image file is released before the + // installer tries to overwrite it. A timeout is reported through the returned + // event, not through err, which stays nil unless the wait itself failed. + event, err := windows.WaitForSingleObject(handle, uint32(processExitWait.Milliseconds())) + if err != nil { + return fmt.Errorf("wait for process exit: %w", err) + } + if event != windows.WAIT_OBJECT_0 { + return fmt.Errorf("wait for process exit: unexpected wait result %#x", event) + } + + return nil +} + +func startUIInSession(uiPath string, sessionID uint32) error { // Get the user token for that session var userToken windows.Token err := windows.WTSQueryUserToken(sessionID, &userToken) @@ -158,6 +307,16 @@ func (u *Installer) startUIAsUser(daemonFolder string) error { } }() + var env *uint16 + if err := windows.CreateEnvironmentBlock(&env, primaryToken, false); err != nil { + return fmt.Errorf("create environment block: %w", err) + } + defer func() { + if err := windows.DestroyEnvironmentBlock(env); err != nil { + log.Warnf("failed to destroy environment block: %v", err) + } + }() + // Prepare startup info var si windows.StartupInfo si.Cb = uint32(unsafe.Sizeof(si)) @@ -180,7 +339,7 @@ func (u *Installer) startUIAsUser(daemonFolder string) error { nil, false, creationFlags, - nil, + env, nil, &si, &pi, @@ -197,7 +356,6 @@ func (u *Installer) startUIAsUser(daemonFolder string) error { log.Warnf("failed to close thread handle: %v", err) } - log.Infof("netbird-ui started successfully in session %d", sessionID) return nil } diff --git a/client/internal/updater/installer/installer_run_windows_test.go b/client/internal/updater/installer/installer_run_windows_test.go new file mode 100644 index 000000000..6a4540610 --- /dev/null +++ b/client/internal/updater/installer/installer_run_windows_test.go @@ -0,0 +1,108 @@ +package installer + +import ( + "errors" + "os/exec" + "slices" + "strconv" + "testing" +) + +// exitErrorWithCode returns a real *exec.ExitError carrying the given exit code. +func exitErrorWithCode(t *testing.T, code int) error { + t.Helper() + + err := exec.Command("cmd.exe", "/c", "exit "+strconv.Itoa(code)).Run() + if err == nil { + t.Fatalf("expected a non-zero exit for code %d", code) + } + return err +} + +func TestIsRebootPending(t *testing.T) { + tests := []struct { + name string + code int + want bool + }{ + {name: "reboot required", code: msiRebootRequired, want: true}, + {name: "reboot initiated", code: msiRebootInitiated, want: true}, + {name: "generic failure", code: 1603, want: false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := isRebootPending(exitErrorWithCode(t, tt.code)); got != tt.want { + t.Errorf("isRebootPending(exit %d) = %v, want %v", tt.code, got, tt.want) + } + }) + } +} + +// TestProcessIDsByNameAndTerminate spawns a long-running system process, finds it +// by name and terminates it, covering the path the updater uses to release the UI +// image file before the installer replaces it. +func TestProcessIDsByNameAndTerminate(t *testing.T) { + cmd := exec.Command("ping.exe", "-n", "60", "127.0.0.1") + if err := cmd.Start(); err != nil { + t.Fatalf("start ping: %v", err) + } + + pid := uint32(cmd.Process.Pid) + killed := false + t.Cleanup(func() { + if !killed { + _ = cmd.Process.Kill() + } + _ = cmd.Wait() + }) + + // Name matching must be case-insensitive: the snapshot reports PING.EXE. + pids, err := processIDsByName("ping.exe") + if err != nil { + t.Fatalf("processIDsByName: %v", err) + } + + if !slices.Contains(pids, pid) { + t.Fatalf("PID %d not among the ping.exe processes found: %v", pid, pids) + } + + if err := terminateProcess(pid); err != nil { + t.Fatalf("terminateProcess: %v", err) + } + killed = true + + // terminateProcess only returns once the handle has signalled, so the process + // is already gone and Wait must not block. It exits with the code passed to + // TerminateProcess, which is 0, so Wait reports no error. + if err := cmd.Wait(); err != nil { + t.Fatalf("wait for terminated ping: %v", err) + } + if !cmd.ProcessState.Exited() { + t.Error("process did not exit after terminateProcess") + } + + remaining, err := processIDsByName("ping.exe") + if err != nil { + t.Fatalf("processIDsByName after terminate: %v", err) + } + if slices.Contains(remaining, pid) { + t.Errorf("PID %d still listed after terminateProcess", pid) + } +} + +func TestProcessIDsByNameNoMatch(t *testing.T) { + pids, err := processIDsByName("netbird-nonexistent-process.exe") + if err != nil { + t.Fatalf("processIDsByName: %v", err) + } + if len(pids) != 0 { + t.Errorf("expected no matches, got %v", pids) + } +} + +func TestIsRebootPendingNonExitError(t *testing.T) { + if isRebootPending(errors.New("start installer: file not found")) { + t.Error("a non-exit error must not be treated as a pending reboot") + } +} diff --git a/client/internal/updater/installer/result.go b/client/internal/updater/installer/result.go index 526c3eb53..55a0d8ac8 100644 --- a/client/internal/updater/installer/result.go +++ b/client/internal/updater/installer/result.go @@ -54,6 +54,12 @@ func (rh *ResultHandler) GetErrorResultReason() string { return "" } +// ClearStaleResult removes a result file left over from a previous installation +// attempt so result watchers cannot read an outdated outcome for the current attempt. +func (rh *ResultHandler) ClearStaleResult() error { + return rh.cleanup() +} + func (rh *ResultHandler) WriteSuccess() error { result := Result{ Success: true, diff --git a/client/internal/updater/manager.go b/client/internal/updater/manager.go index 7fc300739..1b69368d0 100644 --- a/client/internal/updater/manager.go +++ b/client/internal/updater/manager.go @@ -435,7 +435,7 @@ func (m *Manager) install(ctx context.Context, pendingVersion *v.Version) error } inst := installer.New() - if err := inst.RunInstallation(ctx, pendingVersion.String()); err != nil { + if err := inst.RunInstallation(ctx, pendingVersion.String()); err != nil { //nolint:staticcheck // always errors on platforms without an installer log.Errorf("error triggering update: %v", err) m.statusRecorder.PublishEvent( cProto.SystemEvent_ERROR, diff --git a/client/ios/NetBirdSDK/client.go b/client/ios/NetBirdSDK/client.go index 5df6f92f1..f92f085ab 100644 --- a/client/ios/NetBirdSDK/client.go +++ b/client/ios/NetBirdSDK/client.go @@ -22,6 +22,8 @@ import ( "github.com/netbirdio/netbird/client/internal/listener" "github.com/netbirdio/netbird/client/internal/peer" "github.com/netbirdio/netbird/client/internal/profilemanager" + "github.com/netbirdio/netbird/client/netstate" + "github.com/netbirdio/netbird/client/netsweep" "github.com/netbirdio/netbird/client/system" "github.com/netbirdio/netbird/formatter" "github.com/netbirdio/netbird/route" @@ -36,11 +38,6 @@ const ( AnonymizeLevelStrict = nbAnonymize.LevelStrictString ) -// ConnectionListener export internal Listener for mobile -type ConnectionListener interface { - peer.Listener -} - // RouteListener export internal RouteListener for mobile type NetworkChangeListener interface { listener.NetworkChangeListener @@ -87,6 +84,12 @@ type Client struct { onHostDnsFn func([]string) dnsManager dns.IosDnsManager loginComplete bool + // netState outlives engine restarts: it mirrors the OS connectivity, not + // the engine lifecycle. Run injects it into each new ConnectClient, which + // distributes it to every reconnection loop. + netState *netstate.State + // sweeper also outlives engine restarts; NotifyNetworkChange sweeps it. + sweeper *netsweep.Sweeper // preloadedConfig holds config loaded from JSON (used on tvOS where file writes are blocked) preloadedConfig *profilemanager.Config @@ -109,6 +112,8 @@ func NewClient(cfgFile, stateFile, cacheDir, logFilePath, deviceName string, osV ctxCancelLock: &sync.Mutex{}, networkChangeListener: networkChangeListener, dnsManager: dnsManager, + netState: netstate.New(), + sweeper: netsweep.New(), } } @@ -184,7 +189,8 @@ func (c *Client) Run(fd int32, interfaceName string, envList *EnvList) error { c.onHostDnsFn = func([]string) {} cfg.WgIface = interfaceName - connectClient := internal.NewConnectClient(ctx, cfg, c.recorder) + connectClient := internal.NewConnectClient(ctx, cfg, c.recorder, + internal.WithNetworkState(c.netState), internal.WithSweeper(c.sweeper)) c.setState(cfg, connectClient) // Persist the latest sync response so DebugBundle can include the network // map. On iOS this is backed by disk to keep it out of the constrained @@ -193,6 +199,25 @@ func (c *Client) Run(fd int32, interfaceName string, envList *EnvList) error { return connectClient.RunOniOS(fd, c.networkChangeListener, c.dnsManager, c.stateFile, c.cacheDir, c.logFilePath) } +// SetNetworkAvailable feeds OS-reported network availability into the client +// (e.g. from NWPathMonitor). While unavailable, the internal reconnect loops +// suspend their attempts and the connection listener reports NoNetwork +// instead of Connecting; when availability returns, the loops resume +// immediately with a fresh backoff. +func (c *Client) SetNetworkAvailable(available bool) { + c.netState.Set(available) + c.recorder.SetNetworkAvailable(available) +} + +// NotifyNetworkChange marks the management, signal and relay connections +// stale after the OS switched networks and schedules a sweep that cuts +// whatever has not redialed on the new network by then. The engine and the +// TUN device stay untouched. +func (c *Client) NotifyNetworkChange() { + c.sweeper.MarkNetworkChange() + log.Infof("network change: connections marked stale") +} + // Stop the internal client and free the resources func (c *Client) Stop() { c.ctxCancelLock.Lock() @@ -331,7 +356,11 @@ func (c *Client) GetStatusDetails() *StatusDetails { // SetConnectionListener set the network connection listener func (c *Client) SetConnectionListener(listener ConnectionListener) { - c.recorder.SetConnectionListener(listener) + if listener == nil { + c.recorder.RemoveConnectionListener() + return + } + c.recorder.SetConnectionListener(connectionListenerAdapter{listener}) } // RemoveConnectionListener remove connection listener diff --git a/client/ios/NetBirdSDK/connection_listener.go b/client/ios/NetBirdSDK/connection_listener.go new file mode 100644 index 000000000..d792537ba --- /dev/null +++ b/client/ios/NetBirdSDK/connection_listener.go @@ -0,0 +1,43 @@ +//go:build ios + +package NetBirdSDK + +import ( + "github.com/netbirdio/netbird/client/internal/peer" +) + +// Client state values, re-exported as basic constants so gomobile emits them +// into the generated bindings. They mirror peer.ClientState*: append-only, +// never reorder. +const ( + ClientStateDisconnected = int(peer.ClientStateDisconnected) + ClientStateConnected = int(peer.ClientStateConnected) + ClientStateConnecting = int(peer.ClientStateConnecting) + ClientStateDisconnecting = int(peer.ClientStateDisconnecting) + ClientStateNoNetwork = int(peer.ClientStateNoNetwork) +) + +// ConnectionListener export internal Listener for mobile. +// +// It intentionally lacks OnStateChanged for now: adding a method to a gomobile +// interface breaks every Swift implementation, so the iOS app keeps building +// against the legacy per-state callbacks. A follow-up will extend it together +// with the app. +type ConnectionListener interface { + OnConnected() + OnDisconnected() + OnConnecting() + OnDisconnecting() + OnAddressChanged(string, string) + OnPeersListChanged(int) +} + +// connectionListenerAdapter adapts the gomobile-facing ConnectionListener to +// peer.Listener. +type connectionListenerAdapter struct { + ConnectionListener +} + +// OnStateChanged is dropped on iOS until the app adopts the state callback; +// the legacy per-state callbacks continue to fire. +func (a connectionListenerAdapter) OnStateChanged(peer.ClientState) {} diff --git a/client/ios/NetBirdSDK/login.go b/client/ios/NetBirdSDK/login.go index 6cba0c411..42a575359 100644 --- a/client/ios/NetBirdSDK/login.go +++ b/client/ios/NetBirdSDK/login.go @@ -323,7 +323,7 @@ func (a *Auth) login(urlOpener URLOpener, forceDeviceAuth bool, deviceName strin const authInfoRequestTimeout = 30 * time.Second func (a *Auth) foregroundGetTokenInfo(authClient *auth.Auth, urlOpener URLOpener, forceDeviceAuth bool) (*auth.TokenInfo, error) { - oAuthFlow, err := authClient.GetOAuthFlow(a.ctx, forceDeviceAuth) + oAuthFlow, err := authClient.GetOAuthFlow(a.ctx, forceDeviceAuth, "") if err != nil { return nil, fmt.Errorf("failed to get OAuth flow: %v", err) } diff --git a/client/netstate/netstate.go b/client/netstate/netstate.go new file mode 100644 index 000000000..0d7a1268b --- /dev/null +++ b/client/netstate/netstate.go @@ -0,0 +1,110 @@ +// Package netstate tracks OS-reported network availability for the client. +// +// A State instance is owned by the platform integration (e.g. the Android or +// iOS bindings, fed from ConnectivityManager callbacks or NWPathMonitor) and +// is injected into the connection retry loops (management, signal, relay, +// peer guards and the top-level connect loop), which consult it to avoid +// burning CPU and battery on reconnect attempts while the device has no +// network at all (e.g. airplane mode), and to reset their backoff as soon as +// the network returns. +// +// Consumers hold a *State that may be nil — every non-mobile platform leaves +// it unset. The read methods are safe on a nil receiver: they report online +// and never block, so consumers behave as if this package did not exist. +package netstate + +import ( + "context" + "sync" + + log "github.com/sirupsen/logrus" +) + +// State holds the OS-reported network availability. The zero value is not +// usable; create instances with New. +type State struct { + mu sync.Mutex + online bool + changed chan struct{} +} + +// New creates a State that starts online. Platforms without network tracking +// pass a nil *State instead: the read methods treat nil as always online and +// never block, so consumers need no nil guards. +func New() *State { + return &State{ + online: true, + changed: make(chan struct{}), + } +} + +// Set records whether the OS reports any usable network. Transitions wake up +// all Wait callers immediately. Unlike the read methods, Set is not nil-safe: +// it is only for the platform owner that created the State with New. +func (s *State) Set(online bool) { + s.mu.Lock() + defer s.mu.Unlock() + if s.online == online { + return + } + s.online = online + close(s.changed) + s.changed = make(chan struct{}) + log.Infof("OS network availability changed: online=%t", online) +} + +// IsOnline reports whether the OS reports at least one usable network. On a +// nil receiver — no State injected — it reports online. +func (s *State) IsOnline() bool { + if s == nil { + return true + } + s.mu.Lock() + defer s.mu.Unlock() + return s.online +} + +// Changed returns a channel closed on the next availability transition, for +// callers that already own a select loop and cannot block in Wait. Re-read it +// after every fire: each transition installs a fresh channel. On a nil +// receiver — no State injected — it returns nil, which blocks forever in a +// select, so the caller simply never observes a transition. +func (s *State) Changed() <-chan struct{} { + if s == nil { + return nil + } + s.mu.Lock() + defer s.mu.Unlock() + return s.changed +} + +// Wait blocks while the network is offline. It reports whether it had to +// wait, so callers can reset their backoff after an outage. It returns early +// with the context error when ctx is done. On a nil receiver — no State +// injected — it returns immediately. +func (s *State) Wait(ctx context.Context) (bool, error) { + if s == nil { + return false, nil + } + waited := false + for { + s.mu.Lock() + if s.online { + s.mu.Unlock() + return waited, nil + } + ch := s.changed + s.mu.Unlock() + + if !waited { + waited = true + log.Debugf("network is offline, pausing connection attempts") + } + + select { + case <-ctx.Done(): + return waited, ctx.Err() + case <-ch: + } + } +} diff --git a/client/netstate/netstate_test.go b/client/netstate/netstate_test.go new file mode 100644 index 000000000..ea7015761 --- /dev/null +++ b/client/netstate/netstate_test.go @@ -0,0 +1,170 @@ +package netstate + +import ( + "context" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNewStateIsOnline(t *testing.T) { + assert.True(t, New().IsOnline(), "a fresh State should start online") +} + +func TestSetTogglesOnlineState(t *testing.T) { + s := New() + + s.Set(false) + assert.False(t, s.IsOnline(), "state should be offline after Set(false)") + + s.Set(true) + assert.True(t, s.IsOnline(), "state should be online after Set(true)") +} + +func TestWaitReturnsImmediatelyWhenOnline(t *testing.T) { + s := New() + + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + waited, err := s.Wait(ctx) + require.NoError(t, err) + assert.False(t, waited, "Wait should not block when the network is online") +} + +func TestWaitBlocksUntilOnline(t *testing.T) { + s := New() + s.Set(false) + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + result := make(chan bool, 1) + go func() { + waited, err := s.Wait(ctx) + if err != nil { + result <- false + return + } + result <- waited + }() + + // Verify Wait is actually blocking while offline + select { + case <-result: + t.Fatal("Wait should block while the network is offline") + case <-time.After(100 * time.Millisecond): + } + + s.Set(true) + + select { + case waited := <-result: + assert.True(t, waited, "Wait should report that it had to wait for the network") + case <-time.After(2 * time.Second): + t.Fatal("Wait should return promptly after the network becomes available") + } +} + +func TestWaitReturnsOnContextCancel(t *testing.T) { + s := New() + s.Set(false) + + ctx, cancel := context.WithCancel(context.Background()) + + result := make(chan error, 1) + go func() { + _, err := s.Wait(ctx) + result <- err + }() + + cancel() + + select { + case err := <-result: + assert.ErrorIs(t, err, context.Canceled) + case <-time.After(2 * time.Second): + t.Fatal("Wait should return promptly after context cancellation") + } +} + +func TestWaitWakesAllWaiters(t *testing.T) { + s := New() + s.Set(false) + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + const waiters = 10 + var wg sync.WaitGroup + results := make(chan bool, waiters) + for i := 0; i < waiters; i++ { + wg.Add(1) + go func() { + defer wg.Done() + waited, err := s.Wait(ctx) + if err != nil { + results <- false + return + } + results <- waited + }() + } + + time.Sleep(100 * time.Millisecond) + s.Set(true) + wg.Wait() + + close(results) + count := 0 + for waited := range results { + assert.True(t, waited, "every waiter should report that it waited") + count++ + } + assert.Equal(t, waiters, count, "all waiters should have returned") +} + +func TestNilStateReadsAreNoops(t *testing.T) { + var s *State + + assert.True(t, s.IsOnline(), "nil State should report online") + + waited, err := s.Wait(context.Background()) + require.NoError(t, err) + assert.False(t, waited, "nil State's Wait should not block") +} + +func TestConcurrentSetAndWait(t *testing.T) { + s := New() + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + var wg sync.WaitGroup + for i := 0; i < 4; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for j := 0; j < 100; j++ { + s.Set(j%2 == 0) + s.IsOnline() + } + }() + } + for i := 0; i < 4; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for j := 0; j < 100; j++ { + if _, err := s.Wait(ctx); err != nil { + return + } + } + }() + } + + wg.Wait() +} diff --git a/client/netsweep/netsweep.go b/client/netsweep/netsweep.go new file mode 100644 index 000000000..46bc0a709 --- /dev/null +++ b/client/netsweep/netsweep.go @@ -0,0 +1,267 @@ +// Package netsweep cuts network-bound activity when the OS switches networks: +// a sweep closes the registered connections and aborts the in-flight dials, so +// their owners redial immediately instead of waiting for the old sockets to +// time out. +// +// A nil *Sweeper disables everything: all methods are nil-safe no-ops. +package netsweep + +import ( + "context" + "errors" + "net" + "sync" + "time" + + "github.com/cenkalti/backoff/v4" + log "github.com/sirupsen/logrus" + + "github.com/netbirdio/netbird/client/netstate" +) + +// DefaultSweepDelay absorbs network flapping while the OS settles on a +// default network before the stale registrations are cut. +const DefaultSweepDelay = 500 * time.Millisecond + +const recentMarkWindow = 3 * time.Second + +// Config customizes a Sweeper. The zero value applies the defaults. +type Config struct { + // SweepDelay overrides DefaultSweepDelay when positive. + SweepDelay time.Duration +} + +// ErrSwept reports that a dial finished after a network change swept its +// registration. The connection is already closed; the caller must treat it +// as a failed dial and redial on the new network. +var ErrSwept = errors.New("netsweep: connection swept by network change") + +// sweepID identifies one registration in a sweeper. Connections and dials +// draw from the same counter, so an id is unique across both registries. +type sweepID uint64 + +type connEntry struct { + conn net.Conn + gen uint64 +} + +// Dial tracks one dial from start to connection registration. It hands the +// dialed connection to the sweeper atomically, so a sweep can never fall +// between the dial finishing and the connection being registered. +type Dial struct { + sweeper *Sweeper + ctx context.Context + cancel context.CancelFunc + id sweepID + done bool // set by a sweep, WrapConn or Release; guarded by sweeper.mu + gen uint64 +} + +// Ctx returns the dial's context. A sweep cancels it, so a dial started on the +// old network aborts instead of waiting out its handshake timeout. +func (d *Dial) Ctx() context.Context { + return d.ctx +} + +// Release ends the dial's registration and cancels its context. It is +// idempotent and safe after WrapConn, so callers can defer it. +func (d *Dial) Release() { + s := d.sweeper + if s == nil { + return + } + + s.mu.Lock() + d.done = true + delete(s.dials, d.id) + s.mu.Unlock() + + d.cancel() +} + +// sweptConn deregisters itself from the sweeper when closed. +type sweptConn struct { + net.Conn + sweeper *Sweeper + id sweepID +} + +func (c *sweptConn) Close() error { + c.sweeper.deregister(c.id) + return c.Conn.Close() +} + +// Sweeper registers live connections and in-flight dials so the +// network-change sweep can cut everything registered before the change. +type Sweeper struct { + mu sync.Mutex + conns map[sweepID]connEntry + dials map[sweepID]*Dial + nextID sweepID + gen uint64 + timer *time.Timer + sweepDelay time.Duration + lastMark time.Time +} + +// New creates an empty sweeper with the default configuration. +func New() *Sweeper { + return NewWithConfig(Config{}) +} + +// NewWithConfig creates an empty sweeper customized by cfg. +func NewWithConfig(cfg Config) *Sweeper { + delay := cfg.SweepDelay + if delay <= 0 { + delay = DefaultSweepDelay + } + return &Sweeper{ + conns: make(map[sweepID]connEntry), + dials: make(map[sweepID]*Dial), + sweepDelay: delay, + } +} + +// StartDial registers an in-flight dial. Dial with Ctx, hand the result to +// WrapConn, and Release the dial when the attempt is over, typically deferred. +func (s *Sweeper) StartDial(ctx context.Context) *Dial { + if s == nil { + return &Dial{ctx: ctx} + } + + ctx, cancel := context.WithCancel(ctx) + d := &Dial{sweeper: s, ctx: ctx, cancel: cancel} + + s.mu.Lock() + d.id = s.nextID + s.nextID++ + d.gen = s.gen + s.dials[d.id] = d + s.mu.Unlock() + + return d +} + +// WrapConn hands conn over to the sweeper. If a sweep ran since StartDial, +// the connection belongs to the old network: it is closed and ErrSwept is +// returned. Otherwise conn is registered against the next sweep and returned +// wrapped, deregistering itself on Close. Call it once, before Release. +func (d *Dial) WrapConn(conn net.Conn) (net.Conn, error) { + s := d.sweeper + if s == nil { + return conn, nil + } + + s.mu.Lock() + if d.done { + s.mu.Unlock() + if err := conn.Close(); err != nil { + log.Debugf("swept dial close error: %v", err) + } + return nil, ErrSwept + } + d.done = true + delete(s.dials, d.id) + id := s.nextID + s.nextID++ + // The conn inherits the dial's generation: the socket was bound to the + // network that was default when the dial started, not when it finished. + s.conns[id] = connEntry{conn: conn, gen: d.gen} + s.mu.Unlock() + + return &sweptConn{Conn: conn, sweeper: s, id: id}, nil +} + +// MarkNetworkChange records that the OS switched networks: everything +// registered so far becomes stale, and a sweep is (re)scheduled after the +// configured delay to cut whatever is still stale by then. Owners that +// redialed in the meantime hold fresh-generation registrations and survive, +// so no cancellation is needed around the sweep. +func (s *Sweeper) MarkNetworkChange() { + if s == nil { + return + } + + s.mu.Lock() + s.gen++ + cutoff := s.gen + s.lastMark = time.Now() + if s.timer != nil { + s.timer.Stop() + } + s.timer = time.AfterFunc(s.sweepDelay, func() { + n := s.sweep(cutoff) + log.Infof("network change sweep: closed %d stale connections", n) + }) + s.mu.Unlock() +} + +// QuickRetryBackoff wraps bo so that after each Reset the first retry comes +// quickly when the disconnect followed a recent network change and the +// network is online. Any other failure keeps bo's spread, so the clients of +// a restarted server still scatter their reconnects. A nil sweeper returns +// bo unchanged. +func (s *Sweeper) QuickRetryBackoff(ctx context.Context, bo backoff.BackOff, netState *netstate.State) backoff.BackOff { + if s == nil { + return bo + } + return backoff.WithContext(newQuickRetryBackoff(bo, s, netState), ctx) +} + +func (s *Sweeper) markedRecently() bool { + if s == nil { + return false + } + s.mu.Lock() + defer s.mu.Unlock() + return !s.lastMark.IsZero() && time.Since(s.lastMark) < recentMarkWindow +} + +// sweep closes the registered connections and aborts the in-flight dials +// older than cutoff, and returns how many connections it closed. A dial +// whose connection was not yet handed to WrapConn is marked, so the late +// WrapConn closes it instead of registering it. +func (s *Sweeper) sweep(cutoff uint64) int { + if s == nil { + return 0 + } + + s.mu.Lock() + var conns []net.Conn + for id, e := range s.conns { + if e.gen < cutoff { + delete(s.conns, id) + conns = append(conns, e.conn) + } + } + var dials []*Dial + for id, d := range s.dials { + if d.gen < cutoff { + d.done = true + delete(s.dials, id) + dials = append(dials, d) + } + } + s.mu.Unlock() + + if len(dials) > 0 { + log.Debugf("aborting %d in-flight dials", len(dials)) + for _, d := range dials { + d.cancel() + } + } + + for _, conn := range conns { + log.Debugf("sweeping connection %s -> %s", conn.LocalAddr(), conn.RemoteAddr()) + if err := conn.Close(); err != nil { + log.Debugf("swept connection close error: %v", err) + } + } + return len(conns) +} + +func (s *Sweeper) deregister(id sweepID) { + s.mu.Lock() + delete(s.conns, id) + s.mu.Unlock() +} diff --git a/client/netsweep/netsweep_test.go b/client/netsweep/netsweep_test.go new file mode 100644 index 000000000..88d660c2d --- /dev/null +++ b/client/netsweep/netsweep_test.go @@ -0,0 +1,241 @@ +package netsweep + +import ( + "context" + "math" + "net" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSweepClosesRegisteredConns(t *testing.T) { + sweeper := New() + + c1 := wrap(t, sweeper, connPair(t)) + c2 := wrap(t, sweeper, connPair(t)) + + assert.Equal(t, 2, sweeper.sweepAll(), "both live connections should be closed") + + // The wrappers must report closed now. + buf := make([]byte, 1) + _, err := c1.Read(buf) + assert.Error(t, err, "first connection should be unusable after the sweep") + _, err = c2.Read(buf) + assert.Error(t, err, "second connection should be unusable after the sweep") + + assert.Equal(t, 0, sweeper.sweepAll(), "second sweep should find nothing") +} + +func TestCloseDeregisters(t *testing.T) { + sweeper := New() + + conn := wrap(t, sweeper, connPair(t)) + require.NoError(t, conn.Close()) + + assert.Equal(t, 0, sweeper.sweepAll(), "closed connection must leave the registry") +} + +func TestCloseIsIdempotent(t *testing.T) { + sweeper := New() + + conn := wrap(t, sweeper, connPair(t)) + require.NoError(t, conn.Close()) + assert.Error(t, conn.Close(), "double close surfaces the underlying error but must not panic") +} + +func TestSweepOnlyAffectsOlderConns(t *testing.T) { + sweeper := New() + + _ = wrap(t, sweeper, connPair(t)) + assert.Equal(t, 1, sweeper.sweepAll()) + + // A connection dialed after the sweep must survive until the next one. + _ = wrap(t, sweeper, connPair(t)) + assert.Equal(t, 1, sweeper.sweepAll(), "post-sweep connection belongs to the next sweep") +} + +func TestSweepAbortsInFlightDials(t *testing.T) { + sweeper := New() + + dial := sweeper.StartDial(context.Background()) + defer dial.Release() + + sweeper.sweepAll() + + assert.ErrorIs(t, dial.Ctx().Err(), context.Canceled, "sweep must cancel the in-flight dial context") +} + +func TestReleasedDialIsNotAborted(t *testing.T) { + sweeper := New() + + // Simulate a dial that finished before the sweep. + released := sweeper.StartDial(context.Background()) + released.Release() + + // A dial still in flight during the sweep. + pending := sweeper.StartDial(context.Background()) + defer pending.Release() + + sweeper.sweepAll() + assert.ErrorIs(t, pending.Ctx().Err(), context.Canceled, "pending dial must be aborted") +} + +func TestSweepBetweenDialAndHandoffClosesConn(t *testing.T) { + sweeper := New() + + dial := sweeper.StartDial(context.Background()) + defer dial.Release() + + // The dial succeeds on the old network, then the sweep lands before the + // connection is handed over. + conn := connPair(t) + assert.Equal(t, 0, sweeper.sweepAll(), "the connection is not registered yet") + + wrapped, err := dial.WrapConn(conn) + require.ErrorIs(t, err, ErrSwept) + require.Nil(t, wrapped) + + buf := make([]byte, 1) + _, err = conn.Read(buf) + assert.Error(t, err, "the old-network connection must be closed, not leaked") + + assert.Equal(t, 0, sweeper.sweepAll(), "nothing may leak into the next sweep") +} + +func TestMarkNetworkChangeSparesFreshConns(t *testing.T) { + sweeper := NewWithConfig(Config{SweepDelay: 10 * time.Millisecond}) + + stale := wrap(t, sweeper, connPair(t)) + sweeper.MarkNetworkChange() + _ = wrap(t, sweeper, connPair(t)) + + _ = stale.SetReadDeadline(time.Now().Add(time.Second)) + buf := make([]byte, 1) + _, err := stale.Read(buf) + require.ErrorIs(t, err, net.ErrClosed, "stale connection must be closed by the delayed sweep") + + assert.Equal(t, 1, sweeper.sweepAll(), "the fresh connection must survive the stale sweep") +} + +func TestMarkNetworkChangeAbortsStaleDials(t *testing.T) { + sweeper := NewWithConfig(Config{SweepDelay: 10 * time.Millisecond}) + + stale := sweeper.StartDial(context.Background()) + defer stale.Release() + sweeper.MarkNetworkChange() + fresh := sweeper.StartDial(context.Background()) + defer fresh.Release() + + assert.Eventually(t, func() bool { + return stale.Ctx().Err() != nil + }, time.Second, 5*time.Millisecond, "stale dial must be aborted by the delayed sweep") + assert.NoError(t, fresh.Ctx().Err(), "post-mark dial must not be aborted") +} + +func TestConnInheritsDialGeneration(t *testing.T) { + sweeper := NewWithConfig(Config{SweepDelay: 20 * time.Millisecond}) + + // The dial starts before the network change but completes after it: the + // socket is bound to the old network, so the sweep must still cut it. + dial := sweeper.StartDial(context.Background()) + defer dial.Release() + sweeper.MarkNetworkChange() + + wrapped, err := dial.WrapConn(connPair(t)) + require.NoError(t, err) + + _ = wrapped.SetReadDeadline(time.Now().Add(time.Second)) + buf := make([]byte, 1) + _, err = wrapped.Read(buf) + require.ErrorIs(t, err, net.ErrClosed, "old-generation connection must be swept") +} + +func TestRepeatedMarksCoalesce(t *testing.T) { + sweeper := NewWithConfig(Config{SweepDelay: 20 * time.Millisecond}) + + first := wrap(t, sweeper, connPair(t)) + sweeper.MarkNetworkChange() + second := wrap(t, sweeper, connPair(t)) + sweeper.MarkNetworkChange() + _ = wrap(t, sweeper, connPair(t)) + + buf := make([]byte, 1) + for _, conn := range []net.Conn{first, second} { + _ = conn.SetReadDeadline(time.Now().Add(time.Second)) + _, err := conn.Read(buf) + require.ErrorIs(t, err, net.ErrClosed, "every pre-mark connection must be swept by the rescheduled sweep") + } + assert.Equal(t, 1, sweeper.sweepAll(), "only the newest-generation connection may remain") +} + +func TestNilSweeperIsNoop(t *testing.T) { + var sweeper *Sweeper + + conn := connPair(t) + dial := sweeper.StartDial(context.Background()) + defer dial.Release() + + wrapped, err := dial.WrapConn(conn) + require.NoError(t, err) + assert.Equal(t, conn, wrapped, "nil sweeper must return the conn unchanged") + assert.NoError(t, dial.Ctx().Err(), "nil sweeper must not cancel the dial context") + assert.Equal(t, 0, sweeper.sweepAll(), "nil sweeper closes nothing") +} + +// wrap registers conn with the sweeper through a completed dial. +func wrap(t *testing.T, sweeper *Sweeper, conn net.Conn) net.Conn { + t.Helper() + + dial := sweeper.StartDial(context.Background()) + defer dial.Release() + + wrapped, err := dial.WrapConn(conn) + require.NoError(t, err) + return wrapped +} + +// connPair dials a loopback TCP connection and keeps the accepted peer open +// until the test ends: a peer that closed early would make the connection +// unreadable on its own, so a read error after the sweep would prove nothing. +func connPair(t *testing.T) net.Conn { + t.Helper() + + l, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err) + t.Cleanup(func() { + if err := l.Close(); err != nil { + t.Logf("listener close error: %v", err) + } + }) + + accepted := make(chan net.Conn, 1) + go func() { + conn, err := l.Accept() + if err != nil { + close(accepted) + return + } + accepted <- conn + }() + + conn, err := net.Dial("tcp", l.Addr().String()) + require.NoError(t, err) + + peer, ok := <-accepted + require.True(t, ok, "listener must accept the dialed connection") + t.Cleanup(func() { + if err := peer.Close(); err != nil { + t.Logf("peer close error: %v", err) + } + }) + + return conn +} + +// sweepAll cuts every registration regardless of generation. +func (s *Sweeper) sweepAll() int { + return s.sweep(math.MaxUint64) +} diff --git a/client/netsweep/quick_retry.go b/client/netsweep/quick_retry.go new file mode 100644 index 000000000..524a5c50c --- /dev/null +++ b/client/netsweep/quick_retry.go @@ -0,0 +1,39 @@ +package netsweep + +import ( + "time" + + "github.com/cenkalti/backoff/v4" + + "github.com/netbirdio/netbird/client/netstate" +) + +const quickRetryDelay = 200 * time.Millisecond + +type quickRetryBackoff struct { + backoff.BackOff + sweeper *Sweeper + netState *netstate.State + used bool +} + +func newQuickRetryBackoff(bo backoff.BackOff, sweeper *Sweeper, netState *netstate.State) *quickRetryBackoff { + return &quickRetryBackoff{ + BackOff: bo, + sweeper: sweeper, + netState: netState, + } +} + +func (b *quickRetryBackoff) NextBackOff() time.Duration { + if !b.used && b.sweeper.markedRecently() && b.netState.IsOnline() { + b.used = true + return quickRetryDelay + } + return b.BackOff.NextBackOff() +} + +func (b *quickRetryBackoff) Reset() { + b.used = false + b.BackOff.Reset() +} diff --git a/client/netsweep/quick_retry_test.go b/client/netsweep/quick_retry_test.go new file mode 100644 index 000000000..5505862c5 --- /dev/null +++ b/client/netsweep/quick_retry_test.go @@ -0,0 +1,58 @@ +package netsweep + +import ( + "context" + "testing" + "time" + + "github.com/cenkalti/backoff/v4" + "github.com/stretchr/testify/assert" +) + +func TestQuickRetryAfterRecentMark(t *testing.T) { + sweeper := New() + sweeper.MarkNetworkChange() + + slow := backoff.NewConstantBackOff(5 * time.Second) + bo := sweeper.QuickRetryBackoff(context.Background(), slow, nil) + + assert.Equal(t, quickRetryDelay, bo.NextBackOff(), "first retry after a mark must be quick") + assert.Equal(t, 5*time.Second, bo.NextBackOff(), "second retry must fall back to the wrapped backoff") + + bo.Reset() + assert.Equal(t, quickRetryDelay, bo.NextBackOff(), "reset must re-arm the quick retry") +} + +func TestQuickRetryWithoutMarkKeepsSpread(t *testing.T) { + sweeper := New() + + slow := backoff.NewConstantBackOff(5 * time.Second) + bo := sweeper.QuickRetryBackoff(context.Background(), slow, nil) + + assert.Equal(t, 5*time.Second, bo.NextBackOff(), "without a mark the wrapped backoff decides") + + sweeper.mu.Lock() + sweeper.lastMark = time.Now().Add(-recentMarkWindow) + sweeper.mu.Unlock() + assert.Equal(t, 5*time.Second, bo.NextBackOff(), "a stale mark must not trigger the quick retry") +} + +func TestQuickRetryNilSweeperPassthrough(t *testing.T) { + var sweeper *Sweeper + + slow := backoff.NewConstantBackOff(5 * time.Second) + bo := sweeper.QuickRetryBackoff(context.Background(), slow, nil) + + assert.Equal(t, backoff.BackOff(slow), bo, "nil sweeper must return the backoff unchanged") +} + +func TestQuickRetryHonorsContext(t *testing.T) { + sweeper := New() + sweeper.MarkNetworkChange() + + ctx, cancel := context.WithCancel(context.Background()) + cancel() + bo := sweeper.QuickRetryBackoff(ctx, backoff.NewConstantBackOff(time.Millisecond), nil) + + assert.Equal(t, backoff.Stop, bo.NextBackOff(), "cancelled context must stop the retry loop") +} diff --git a/client/server/panic_windows.go b/client/server/panic_windows.go index 8592f12ad..4bed6662f 100644 --- a/client/server/panic_windows.go +++ b/client/server/panic_windows.go @@ -3,6 +3,7 @@ package server import ( + "errors" "fmt" "os" "path" @@ -69,7 +70,7 @@ func setStdHandle(f *os.File) error { handle := f.Fd() r0, _, e1 := setStdHandleFn.Call(stdErrorHandle, handle) if r0 == 0 { - if e1 != nil { + if !errors.Is(e1, syscall.Errno(0)) { return e1 } return syscall.EINVAL diff --git a/client/server/server_privileged_test.go b/client/server/server_privileged_test.go index 8b6f78f04..0366ccb31 100644 --- a/client/server/server_privileged_test.go +++ b/client/server/server_privileged_test.go @@ -10,7 +10,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel" diff --git a/client/ssh/client/client.go b/client/ssh/client/client.go index 4180849cd..31143a4f4 100644 --- a/client/ssh/client/client.go +++ b/client/ssh/client/client.go @@ -313,21 +313,23 @@ func Dial(ctx context.Context, addr, user string, opts DialOptions) (*Client, er // dialSSH establishes an SSH connection without JWT authentication func dialSSH(ctx context.Context, network, addr string, config *ssh.ClientConfig) (*Client, error) { + if config.Timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, config.Timeout) + defer cancel() + } + dialer := &net.Dialer{} conn, err := dialer.DialContext(ctx, network, addr) if err != nil { return nil, fmt.Errorf("dial %s: %w", addr, err) } - clientConn, chans, reqs, err := ssh.NewClientConn(conn, addr, config) + client, err := nbssh.Handshake(ctx, conn, addr, config) if err != nil { - if closeErr := conn.Close(); closeErr != nil { - log.Debugf("connection close after handshake failure: %v", closeErr) - } - return nil, fmt.Errorf("ssh handshake: %w", err) + return nil, err } - client := ssh.NewClient(clientConn, chans, reqs) return &Client{ client: client, }, nil diff --git a/client/ssh/client/terminal_unix.go b/client/ssh/client/terminal_unix.go index aaa3418f9..a963dc8be 100644 --- a/client/ssh/client/terminal_unix.go +++ b/client/ssh/client/terminal_unix.go @@ -12,6 +12,8 @@ import ( log "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh" "golang.org/x/term" + + nbssh "github.com/netbirdio/netbird/client/ssh" ) func (c *Client) setupTerminalMode(ctx context.Context, session *ssh.Session) error { @@ -82,37 +84,7 @@ func (c *Client) setupTerminal(session *ssh.Session, fd int) error { return fmt.Errorf("get terminal size: %w", err) } - modes := ssh.TerminalModes{ - ssh.ECHO: 1, - ssh.TTY_OP_ISPEED: 14400, - ssh.TTY_OP_OSPEED: 14400, - // Ctrl+C - ssh.VINTR: 3, - // Ctrl+\ - ssh.VQUIT: 28, - // Backspace - ssh.VERASE: 127, - // Ctrl+U - ssh.VKILL: 21, - // Ctrl+D - ssh.VEOF: 4, - ssh.VEOL: 0, - ssh.VEOL2: 0, - // Ctrl+Q - ssh.VSTART: 17, - // Ctrl+S - ssh.VSTOP: 19, - // Ctrl+Z - ssh.VSUSP: 26, - // Ctrl+O - ssh.VDISCARD: 15, - // Ctrl+R - ssh.VREPRINT: 18, - // Ctrl+W - ssh.VWERASE: 23, - // Ctrl+V - ssh.VLNEXT: 22, - } + modes := nbssh.DefaultTerminalModes terminal := os.Getenv("TERM") if terminal == "" { diff --git a/client/ssh/client/terminal_windows.go b/client/ssh/client/terminal_windows.go index 462438317..c6156fc26 100644 --- a/client/ssh/client/terminal_windows.go +++ b/client/ssh/client/terminal_windows.go @@ -10,6 +10,8 @@ import ( log "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh" + + nbssh "github.com/netbirdio/netbird/client/ssh" ) const ( @@ -80,28 +82,14 @@ func (c *Client) setupTerminalMode(_ context.Context, session *ssh.Session) erro w, h := c.getWindowsConsoleSize() modes := ssh.TerminalModes{ - ssh.ECHO: 1, - ssh.TTY_OP_ISPEED: 14400, - ssh.TTY_OP_OSPEED: 14400, - ssh.ICRNL: 1, - ssh.OPOST: 1, - ssh.ONLCR: 1, - ssh.ISIG: 1, - ssh.ICANON: 1, - ssh.VINTR: 3, // Ctrl+C - ssh.VQUIT: 28, // Ctrl+\ - ssh.VERASE: 127, // Backspace - ssh.VKILL: 21, // Ctrl+U - ssh.VEOF: 4, // Ctrl+D - ssh.VEOL: 0, - ssh.VEOL2: 0, - ssh.VSTART: 17, // Ctrl+Q - ssh.VSTOP: 19, // Ctrl+S - ssh.VSUSP: 26, // Ctrl+Z - ssh.VDISCARD: 15, // Ctrl+O - ssh.VWERASE: 23, // Ctrl+W - ssh.VLNEXT: 22, // Ctrl+V - ssh.VREPRINT: 18, // Ctrl+R + ssh.ICRNL: 1, + ssh.OPOST: 1, + ssh.ONLCR: 1, + ssh.ISIG: 1, + ssh.ICANON: 1, + } + for mode, value := range nbssh.DefaultTerminalModes { + modes[mode] = value } if err := session.RequestPty("xterm-256color", h, w, modes); err != nil { diff --git a/client/ssh/common.go b/client/ssh/common.go index 3f4f3e9d1..4ebf8842a 100644 --- a/client/ssh/common.go +++ b/client/ssh/common.go @@ -35,6 +35,19 @@ type HostKeyVerifier interface { VerifySSHHostKey(peerAddress string, key []byte) error } +// PeerKeyLookup returns the stored SSH host key for a peer address. +type PeerKeyLookup func(peerAddress string) ([]byte, bool) + +// VerifySSHHostKey implements HostKeyVerifier by looking up the stored key +// and comparing it against the presented key. +func (l PeerKeyLookup) VerifySSHHostKey(peerAddress string, presentedKey []byte) error { + storedKey, found := l(peerAddress) + if !found { + return ErrPeerNotFound + } + return VerifyHostKey(storedKey, presentedKey, peerAddress) +} + // DaemonHostKeyVerifier implements HostKeyVerifier using the NetBird daemon type DaemonHostKeyVerifier struct { client proto.DaemonServiceClient diff --git a/client/ssh/handshake.go b/client/ssh/handshake.go new file mode 100644 index 000000000..e78a806be --- /dev/null +++ b/client/ssh/handshake.go @@ -0,0 +1,45 @@ +package ssh + +import ( + "context" + "fmt" + "io" + "net" + "time" + + log "github.com/sirupsen/logrus" + "golang.org/x/crypto/ssh" +) + +// Handshake runs the SSH client handshake on an already dialed conn and +// returns the resulting client. Dialing bounds only the TCP establishment; +// without a deadline on the socket a peer that accepts and then goes silent +// blocks the handshake forever, so the context deadline is applied to conn +// for the duration of the handshake. conn is closed on any error. +func Handshake(ctx context.Context, conn net.Conn, addr string, config *ssh.ClientConfig) (*ssh.Client, error) { + if deadline, ok := ctx.Deadline(); ok { + if err := conn.SetDeadline(deadline); err != nil { + closeHandshake(conn, "conn after deadline error") + return nil, fmt.Errorf("set handshake deadline: %w", err) + } + } + + sshConn, chans, reqs, err := ssh.NewClientConn(conn, addr, config) + if err != nil { + closeHandshake(conn, "conn after handshake error") + return nil, fmt.Errorf("ssh handshake: %w", err) + } + + if err := conn.SetDeadline(time.Time{}); err != nil { + closeHandshake(sshConn, "ssh conn after deadline clear error") + return nil, fmt.Errorf("clear handshake deadline: %w", err) + } + + return ssh.NewClient(sshConn, chans, reqs), nil +} + +func closeHandshake(c io.Closer, label string) { + if err := c.Close(); err != nil { + log.Debugf("ssh: close %s: %v", label, err) + } +} diff --git a/client/ssh/proxy/proxy.go b/client/ssh/proxy/proxy.go index 721810edb..070515b57 100644 --- a/client/ssh/proxy/proxy.go +++ b/client/ssh/proxy/proxy.go @@ -610,13 +610,10 @@ func (p *SSHProxy) dialBackend(ctx context.Context, addr, user, jwtToken string) return nil, fmt.Errorf("connect to server: %w", err) } - clientConn, chans, reqs, err := cryptossh.NewClientConn(conn, addr, config) - if err != nil { - _ = conn.Close() - return nil, fmt.Errorf("SSH handshake: %w", err) - } + handshakeCtx, cancel := context.WithTimeout(ctx, sshHandshakeTimeout) + defer cancel() - return cryptossh.NewClient(clientConn, chans, reqs), nil + return nbssh.Handshake(handshakeCtx, conn, addr, config) } func (p *SSHProxy) verifyHostKey(hostname string, remote net.Addr, key cryptossh.PublicKey) error { diff --git a/client/ssh/server/command_execution.go b/client/ssh/server/command_execution.go index b0a85fe4b..c8b3240d0 100644 --- a/client/ssh/server/command_execution.go +++ b/client/ssh/server/command_execution.go @@ -75,8 +75,8 @@ func (s *Server) createCommand(logger *log.Entry, privilegeResult PrivilegeCheck } // Try su first for system integration (PAM/audit) when privileged - cmd, err := s.createSuCommand(logger, session, localUser, hasPty) - if err != nil || privilegeResult.UsedFallback { + cmd, err := s.createSuCommand(logger, session, localUser, hasPty) //nolint:staticcheck + if err != nil || privilegeResult.UsedFallback { //nolint:staticcheck // always errors on platforms without su logger.Debugf("su command failed, falling back to executor: %v", err) cmd, cleanup, err := s.createExecutorCommand(logger, session, localUser, hasPty) if err != nil { diff --git a/client/ssh/session.go b/client/ssh/session.go new file mode 100644 index 000000000..999b6f251 --- /dev/null +++ b/client/ssh/session.go @@ -0,0 +1,84 @@ +package ssh + +import ( + "fmt" + "io" + + log "github.com/sirupsen/logrus" + "golang.org/x/crypto/ssh" +) + +// DefaultTerminalModes are the PTY modes used by the interactive terminal clients. +var DefaultTerminalModes = ssh.TerminalModes{ + ssh.ECHO: 1, + ssh.TTY_OP_ISPEED: 14400, + ssh.TTY_OP_OSPEED: 14400, + ssh.VINTR: 3, // Ctrl+C + ssh.VQUIT: 28, // Ctrl+\ + ssh.VERASE: 127, // Backspace + ssh.VKILL: 21, // Ctrl+U + ssh.VEOF: 4, // Ctrl+D + ssh.VEOL: 0, + ssh.VEOL2: 0, + ssh.VSTART: 17, // Ctrl+Q + ssh.VSTOP: 19, // Ctrl+S + ssh.VSUSP: 26, // Ctrl+Z + ssh.VDISCARD: 15, // Ctrl+O + ssh.VREPRINT: 18, // Ctrl+R + ssh.VWERASE: 23, // Ctrl+W + ssh.VLNEXT: 22, // Ctrl+V +} + +// PTYSession is an interactive shell session with a PTY and its I/O pipes. +type PTYSession struct { + Session *ssh.Session + Stdin io.WriteCloser + Stdout io.Reader + Stderr io.Reader +} + +// StartPTYSession opens a session on the client, requests an xterm-256color PTY +// with the default terminal modes, wires up the I/O pipes and starts a shell. +// The session is closed on any error. +func StartPTYSession(client *ssh.Client, cols, rows int) (*PTYSession, error) { + session, err := client.NewSession() + if err != nil { + return nil, fmt.Errorf("new session: %w", err) + } + + pty, err := setupPTYSession(session, cols, rows) + if err != nil { + if closeErr := session.Close(); closeErr != nil { + log.Debugf("ssh: session close after setup error: %v", closeErr) + } + return nil, err + } + return pty, nil +} + +// setupPTYSession requests the PTY, opens the pipes and starts the shell on an +// already created session. +func setupPTYSession(session *ssh.Session, cols, rows int) (*PTYSession, error) { + if err := session.RequestPty("xterm-256color", rows, cols, DefaultTerminalModes); err != nil { + return nil, fmt.Errorf("request pty: %w", err) + } + + stdin, err := session.StdinPipe() + if err != nil { + return nil, fmt.Errorf("stdin pipe: %w", err) + } + stdout, err := session.StdoutPipe() + if err != nil { + return nil, fmt.Errorf("stdout pipe: %w", err) + } + stderr, err := session.StderrPipe() + if err != nil { + return nil, fmt.Errorf("stderr pipe: %w", err) + } + + if err := session.Shell(); err != nil { + return nil, fmt.Errorf("start shell: %w", err) + } + + return &PTYSession{Session: session, Stdin: stdin, Stdout: stdout, Stderr: stderr}, nil +} diff --git a/client/system/info_android.go b/client/system/info_android.go index 3c71573bb..d4f479386 100644 --- a/client/system/info_android.go +++ b/client/system/info_android.go @@ -30,6 +30,11 @@ func GetInfo(ctx context.Context) *Info { kernelVersion = osInfo[2] } + addrs, err := networkAddresses() + if err != nil { + log.Warnf("discover network addresses: %s", err) + } + gio := &Info{ GoOS: runtime.GOOS, Kernel: kernel, @@ -41,6 +46,7 @@ func GetInfo(ctx context.Context) *Info { NetbirdVersion: version.NetbirdVersion(), UIVersion: extractUIVersion(ctx), KernelVersion: kernelVersion, + NetworkAddresses: addrs, SystemSerialNumber: serial(), SystemProductName: productModel(), SystemManufacturer: productManufacturer(), diff --git a/client/system/info_js.go b/client/system/info_js.go index f32532881..3323fb542 100644 --- a/client/system/info_js.go +++ b/client/system/info_js.go @@ -15,7 +15,7 @@ func UpdateStaticInfoAsync() { } // GetInfo retrieves system information for WASM environment -func GetInfo(_ context.Context) *Info { +func GetInfo(ctx context.Context) *Info { info := &Info{ GoOS: runtime.GOOS, Kernel: runtime.GOARCH, @@ -30,6 +30,13 @@ func GetInfo(_ context.Context) *Info { collectBrowserInfo(info) collectLocationInfo(info) collectSystemInfo(info) + + // A caller-provided device name wins, as on the other platforms. A peer + // registered over an API keeps reporting the name it was registered with, + // so its meta does not change on the first sync. + if name := extractDeviceName(ctx, info.Hostname); name != "" { + info.Hostname = name + } return info } diff --git a/client/system/info_js_test.go b/client/system/info_js_test.go new file mode 100644 index 000000000..e2a33ada0 --- /dev/null +++ b/client/system/info_js_test.go @@ -0,0 +1,27 @@ +//go:build js + +package system + +import ( + "context" + "testing" +) + +// TestGetInfoHonorsDeviceName covers a caller-provided device name reaching the +// reported hostname, so a peer registered over an API keeps reporting the name +// it was registered with instead of renaming itself on its first sync. +func TestGetInfoHonorsDeviceName(t *testing.T) { + ctx := context.WithValue(context.Background(), DeviceNameCtxKey, "session-name") + if got := GetInfo(ctx).Hostname; got != "session-name" { + t.Errorf("hostname should carry the caller's device name, got %q", got) + } +} + +// TestGetInfoWithoutDeviceNameKeepsFallback covers the embed layer's habit of +// always setting the context value: an empty name must not blank the hostname. +func TestGetInfoWithoutDeviceNameKeepsFallback(t *testing.T) { + ctx := context.WithValue(context.Background(), DeviceNameCtxKey, "") + if got := GetInfo(ctx).Hostname; got == "" { + t.Error("an empty device name must not blank the hostname") + } +} diff --git a/client/system/network_addr.go b/client/system/network_addr.go index 44260a938..505a6f0ea 100644 --- a/client/system/network_addr.go +++ b/client/system/network_addr.go @@ -1,4 +1,4 @@ -//go:build !ios +//go:build !ios && !android package system diff --git a/client/system/network_addr_android.go b/client/system/network_addr_android.go new file mode 100644 index 000000000..99a71e105 --- /dev/null +++ b/client/system/network_addr_android.go @@ -0,0 +1,89 @@ +package system + +import ( + "net/netip" + "strings" +) + +var iFaceDiscover IFaceDiscover + +type IFaceDiscover interface { + IFaces() (string, error) +} + +// SetIFaceDiscover configures the Android interface discovery provider. +func SetIFaceDiscover(discover IFaceDiscover) { + iFaceDiscover = discover +} + +func networkAddresses() ([]NetworkAddress, error) { + if iFaceDiscover == nil { + return nil, nil + } + ifaces, err := iFaceDiscover.IFaces() + if err != nil { + return nil, err + } + + var netAddresses []NetworkAddress + for _, line := range strings.Split(ifaces, "\n") { + addresses, ok := interfaceAddresses(line) + if !ok { + continue + } + for _, address := range addresses { + netAddr, ok := toNetworkAddress(address) + if !ok { + continue + } + if isDuplicated(netAddresses, netAddr) { + continue + } + netAddresses = append(netAddresses, netAddr) + } + } + return netAddresses, nil +} + +func interfaceAddresses(line string) ([]string, bool) { + parts := strings.Split(line, "|") + if len(parts) != 2 { + return nil, false + } + flags := strings.Fields(parts[0]) + if len(flags) != 8 { + return nil, false + } + up, loopback := flags[3], flags[5] + if up != "true" || loopback == "true" { + return nil, false + } + return strings.Fields(parts[1]), true +} + +func toNetworkAddress(address string) (NetworkAddress, bool) { + prefix, err := netip.ParsePrefix(address) + if err != nil { + return NetworkAddress{}, false + } + if prefix.Addr().Is4In6() { + if prefix.Bits() < 96 { + return NetworkAddress{}, false + } + prefix = netip.PrefixFrom(prefix.Addr().Unmap(), prefix.Bits()-96) + } + ip := prefix.Addr() + if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsMulticast() { + return NetworkAddress{}, false + } + return NetworkAddress{NetIP: prefix}, true +} + +func isDuplicated(addresses []NetworkAddress, addr NetworkAddress) bool { + for _, duplicated := range addresses { + if duplicated.NetIP == addr.NetIP { + return true + } + } + return false +} diff --git a/client/system/network_addr_test.go b/client/system/network_addr_test.go index a5f9c4279..b0be40f0a 100644 --- a/client/system/network_addr_test.go +++ b/client/system/network_addr_test.go @@ -1,4 +1,4 @@ -//go:build !ios +//go:build !ios && !android package system diff --git a/client/system/process_test.go b/client/system/process_test.go index 9d0a6b935..de1cfc1db 100644 --- a/client/system/process_test.go +++ b/client/system/process_test.go @@ -1,3 +1,5 @@ +//go:build windows || (linux && !android) || (darwin && !ios) || freebsd + package system import ( diff --git a/client/ui/i18n/locales/de/common.json b/client/ui/i18n/locales/de/common.json index d02589591..1208a37fe 100644 --- a/client/ui/i18n/locales/de/common.json +++ b/client/ui/i18n/locales/de/common.json @@ -764,7 +764,19 @@ "message": "Sensible Informationen anonymisieren" }, "settings.troubleshooting.anonymize.help": { - "message": "Versteckt öffentliche IP-Adressen und nicht-NetBird-Domains in Logs." + "message": "Verbirgt IP-Adressen, Domains und andere sensible Werte." + }, + "settings.troubleshooting.anonymize.info": { + "message": "Der Standardmodus lässt interne IPv4-Adressen und Peer-Namen für den Support lesbar. Der strikte Modus anonymisiert zusätzlich private (RFC 1918), CGNAT- und Link-Local-IP-Adressen, Peer-Namen und öffentliche WireGuard-Schlüssel. Wiederkehrende Werte erhalten denselben Platzhalter, sodass Peers unterscheidbar bleiben. Verwenden Sie den strikten Modus, wenn Sie das Debug-Paket außerhalb Ihrer Organisation weitergeben." + }, + "settings.troubleshooting.anonymize.none": { + "message": "Keine" + }, + "settings.troubleshooting.anonymize.default": { + "message": "Standard" + }, + "settings.troubleshooting.anonymize.strict": { + "message": "Strikt" }, "settings.troubleshooting.systemInfo.label": { "message": "Systeminformationen einschließen" @@ -1338,5 +1350,14 @@ }, "error.unknown": { "message": "Vorgang fehlgeschlagen." + }, + "settings.ssh.privilege.hint": { + "message": "Erfordert {actor}. Führen Sie stattdessen dies aus:" + }, + "settings.ssh.privilege.oneWay": { + "message": "Sie können dies deaktivieren, aber zum erneuten Aktivieren sind {actor} erforderlich:" + }, + "settings.ssh.privilege.oneWayInverted": { + "message": "Sie können dies aktivieren, aber zum erneuten Deaktivieren sind {actor} erforderlich:" } } diff --git a/client/ui/i18n/locales/es/common.json b/client/ui/i18n/locales/es/common.json index 3420b612b..6dc4ffd0b 100644 --- a/client/ui/i18n/locales/es/common.json +++ b/client/ui/i18n/locales/es/common.json @@ -764,7 +764,19 @@ "message": "Anonimizar información sensible" }, "settings.troubleshooting.anonymize.help": { - "message": "Oculta las direcciones IP públicas y los dominios ajenos a NetBird de los registros." + "message": "Oculta direcciones IP, dominios y otros valores sensibles." + }, + "settings.troubleshooting.anonymize.info": { + "message": "El modo predeterminado mantiene legibles las direcciones IPv4 internas y los nombres de los peers para el soporte. El modo estricto anonimiza además las direcciones IP privadas (RFC 1918), CGNAT y de enlace local, los nombres de los peers y las claves públicas de WireGuard. Los valores recurrentes se asignan al mismo marcador de posición, por lo que los peers siguen siendo distinguibles. Use el modo estricto cuando comparta el paquete de diagnóstico fuera de su organización." + }, + "settings.troubleshooting.anonymize.none": { + "message": "Ninguno" + }, + "settings.troubleshooting.anonymize.default": { + "message": "Predeterminado" + }, + "settings.troubleshooting.anonymize.strict": { + "message": "Estricto" }, "settings.troubleshooting.systemInfo.label": { "message": "Incluir información del sistema" @@ -1338,5 +1350,14 @@ }, "error.unknown": { "message": "La operación falló." + }, + "settings.ssh.privilege.hint": { + "message": "Requiere {actor}. Ejecute esto en su lugar:" + }, + "settings.ssh.privilege.oneWay": { + "message": "Puede desactivarlo, pero volver a activarlo requiere {actor}:" + }, + "settings.ssh.privilege.oneWayInverted": { + "message": "Puede activarlo, pero volver a desactivarlo requiere {actor}:" } } diff --git a/client/ui/i18n/locales/fr/common.json b/client/ui/i18n/locales/fr/common.json index a83f85c12..d3e54440c 100644 --- a/client/ui/i18n/locales/fr/common.json +++ b/client/ui/i18n/locales/fr/common.json @@ -764,7 +764,19 @@ "message": "Anonymiser les informations sensibles" }, "settings.troubleshooting.anonymize.help": { - "message": "Masque les adresses IP publiques et les domaines non-NetBird dans les journaux." + "message": "Masque les adresses IP, les domaines et d'autres valeurs sensibles." + }, + "settings.troubleshooting.anonymize.info": { + "message": "Le mode par défaut garde les adresses IPv4 internes et les noms des pairs lisibles pour le support. Le mode strict anonymise en plus les adresses IP privées (RFC 1918), CGNAT et de lien local, les noms des pairs et les clés publiques WireGuard. Les valeurs récurrentes reçoivent le même espace réservé, les pairs restent donc distinguables. Utilisez le mode strict lorsque vous partagez le lot de diagnostic en dehors de votre organisation." + }, + "settings.troubleshooting.anonymize.none": { + "message": "Aucune" + }, + "settings.troubleshooting.anonymize.default": { + "message": "Par défaut" + }, + "settings.troubleshooting.anonymize.strict": { + "message": "Strict" }, "settings.troubleshooting.systemInfo.label": { "message": "Inclure les informations système" @@ -1338,5 +1350,14 @@ }, "error.unknown": { "message": "L’opération a échoué." + }, + "settings.ssh.privilege.hint": { + "message": "Nécessite {actor}. Exécutez plutôt ceci :" + }, + "settings.ssh.privilege.oneWay": { + "message": "Vous pouvez le désactiver, mais le réactiver nécessite {actor} :" + }, + "settings.ssh.privilege.oneWayInverted": { + "message": "Vous pouvez l’activer, mais le désactiver de nouveau nécessite {actor} :" } } diff --git a/client/ui/i18n/locales/hu/common.json b/client/ui/i18n/locales/hu/common.json index b291f7a01..19aede17f 100644 --- a/client/ui/i18n/locales/hu/common.json +++ b/client/ui/i18n/locales/hu/common.json @@ -764,7 +764,19 @@ "message": "Érzékeny információk anonimizálása" }, "settings.troubleshooting.anonymize.help": { - "message": "Elrejti a nyilvános IP-címeket és a nem-NetBird tartományokat a naplókban." + "message": "Elrejti az IP-címeket, a tartományokat és más érzékeny értékeket." + }, + "settings.troubleshooting.anonymize.info": { + "message": "Az Alapértelmezett szint a belső IPv4-címeket és a peer-neveket olvashatóan hagyja a támogatás számára. A Szigorú ezen felül anonimizálja a privát (RFC 1918), CGNAT és link-local IP-címeket, a peer-neveket és a WireGuard nyilvános kulcsokat. Az ismétlődő értékek ugyanazt a helyettesítőt kapják, így a peerek megkülönböztethetők maradnak. Használja a Szigorú szintet, ha a hibakeresési csomagot a szervezetén kívül osztja meg." + }, + "settings.troubleshooting.anonymize.none": { + "message": "Nincs" + }, + "settings.troubleshooting.anonymize.default": { + "message": "Alapértelmezett" + }, + "settings.troubleshooting.anonymize.strict": { + "message": "Szigorú" }, "settings.troubleshooting.systemInfo.label": { "message": "Rendszerinformációk beillesztése" @@ -1338,5 +1350,14 @@ }, "error.unknown": { "message": "A művelet meghiúsult." + }, + "settings.ssh.privilege.hint": { + "message": "{actor} szükséges hozzá. Futtassa inkább ezt:" + }, + "settings.ssh.privilege.oneWay": { + "message": "Kikapcsolhatja, de a visszakapcsolásához {actor} szükséges:" + }, + "settings.ssh.privilege.oneWayInverted": { + "message": "Bekapcsolhatja, de az ismételt kikapcsolásához {actor} szükséges:" } } diff --git a/client/ui/i18n/locales/it/common.json b/client/ui/i18n/locales/it/common.json index a68a8b32b..dab9e0cb4 100644 --- a/client/ui/i18n/locales/it/common.json +++ b/client/ui/i18n/locales/it/common.json @@ -764,7 +764,19 @@ "message": "Anonimizza informazioni sensibili" }, "settings.troubleshooting.anonymize.help": { - "message": "Nasconde gli indirizzi IP pubblici e i domini non NetBird dai log." + "message": "Nasconde indirizzi IP, domini e altri valori sensibili." + }, + "settings.troubleshooting.anonymize.info": { + "message": "La modalità predefinita mantiene leggibili gli indirizzi IPv4 interni e i nomi dei peer per il supporto. La modalità rigorosa anonimizza inoltre gli indirizzi IP privati (RFC 1918), CGNAT e link-local, i nomi dei peer e le chiavi pubbliche WireGuard. I valori ricorrenti vengono associati allo stesso segnaposto, quindi i peer restano distinguibili. Usa la modalità rigorosa quando condividi il pacchetto di debug al di fuori della tua organizzazione." + }, + "settings.troubleshooting.anonymize.none": { + "message": "Nessuna" + }, + "settings.troubleshooting.anonymize.default": { + "message": "Predefinito" + }, + "settings.troubleshooting.anonymize.strict": { + "message": "Rigoroso" }, "settings.troubleshooting.systemInfo.label": { "message": "Includi informazioni di sistema" @@ -1338,5 +1350,14 @@ }, "error.unknown": { "message": "Operazione non riuscita." + }, + "settings.ssh.privilege.hint": { + "message": "Richiede {actor}. Esegua invece questo:" + }, + "settings.ssh.privilege.oneWay": { + "message": "Può disabilitarlo, ma riabilitarlo richiede {actor}:" + }, + "settings.ssh.privilege.oneWayInverted": { + "message": "Può abilitarlo, ma disabilitarlo di nuovo richiede {actor}:" } } diff --git a/client/ui/i18n/locales/ja/common.json b/client/ui/i18n/locales/ja/common.json index ec69de9a5..246c232a8 100644 --- a/client/ui/i18n/locales/ja/common.json +++ b/client/ui/i18n/locales/ja/common.json @@ -764,7 +764,19 @@ "message": "機密情報を匿名化" }, "settings.troubleshooting.anonymize.help": { - "message": "ログからパブリック IP アドレスと NetBird 以外のドメインを隠します。" + "message": "IP アドレス、ドメイン、その他の機密性の高い値を隠します。" + }, + "settings.troubleshooting.anonymize.info": { + "message": "「デフォルト」では、サポートのために内部 IPv4 アドレスとピア名は読める状態のまま残ります。「厳格」では、さらにプライベート (RFC 1918)、CGNAT、リンクローカルの IP アドレス、ピア名、WireGuard 公開鍵も匿名化されます。繰り返し現れる値は同じプレースホルダーに置き換えられるため、ピアは区別できます。デバッグバンドルを組織外に共有する場合は「厳格」を使用してください。" + }, + "settings.troubleshooting.anonymize.none": { + "message": "なし" + }, + "settings.troubleshooting.anonymize.default": { + "message": "デフォルト" + }, + "settings.troubleshooting.anonymize.strict": { + "message": "厳格" }, "settings.troubleshooting.systemInfo.label": { "message": "システム情報を含める" @@ -1338,5 +1350,14 @@ }, "error.unknown": { "message": "操作に失敗しました。" + }, + "settings.ssh.privilege.hint": { + "message": "{actor}が必要です。代わりに次のコマンドを実行してください:" + }, + "settings.ssh.privilege.oneWay": { + "message": "無効にはできますが、再度有効にするには{actor}が必要です:" + }, + "settings.ssh.privilege.oneWayInverted": { + "message": "有効にはできますが、再度無効にするには{actor}が必要です:" } } diff --git a/client/ui/i18n/locales/pt/common.json b/client/ui/i18n/locales/pt/common.json index ef1bfd372..418e93717 100644 --- a/client/ui/i18n/locales/pt/common.json +++ b/client/ui/i18n/locales/pt/common.json @@ -764,7 +764,19 @@ "message": "Anonimizar informações sensíveis" }, "settings.troubleshooting.anonymize.help": { - "message": "Oculta endereços IP públicos e domínios que não são do NetBird nos logs." + "message": "Oculta endereços IP, domínios e outros valores sensíveis." + }, + "settings.troubleshooting.anonymize.info": { + "message": "O modo padrão mantém os endereços IPv4 internos e os nomes dos peers legíveis para o suporte. O modo estrito anonimiza também os endereços IP privados (RFC 1918), CGNAT e link-local, os nomes dos peers e as chaves públicas do WireGuard. Valores recorrentes recebem o mesmo marcador, então os peers continuam distinguíveis. Use o modo estrito ao compartilhar o pacote de depuração fora da sua organização." + }, + "settings.troubleshooting.anonymize.none": { + "message": "Nenhum" + }, + "settings.troubleshooting.anonymize.default": { + "message": "Padrão" + }, + "settings.troubleshooting.anonymize.strict": { + "message": "Estrito" }, "settings.troubleshooting.systemInfo.label": { "message": "Incluir informações do sistema" @@ -1338,5 +1350,14 @@ }, "error.unknown": { "message": "A operação falhou." + }, + "settings.ssh.privilege.hint": { + "message": "Requer {actor}. Execute isto em vez disso:" + }, + "settings.ssh.privilege.oneWay": { + "message": "Você pode desativar isto, mas ativar novamente requer {actor}:" + }, + "settings.ssh.privilege.oneWayInverted": { + "message": "Você pode ativar isto, mas desativar novamente requer {actor}:" } } diff --git a/client/ui/i18n/locales/ru/common.json b/client/ui/i18n/locales/ru/common.json index a876387f4..958b5a21c 100644 --- a/client/ui/i18n/locales/ru/common.json +++ b/client/ui/i18n/locales/ru/common.json @@ -764,7 +764,19 @@ "message": "Анонимизировать конфиденциальную информацию" }, "settings.troubleshooting.anonymize.help": { - "message": "Скрывает публичные IP-адреса и сторонние (не относящиеся к NetBird) домены в журналах." + "message": "Скрывает IP-адреса, домены и другие конфиденциальные значения." + }, + "settings.troubleshooting.anonymize.info": { + "message": "Режим «По умолчанию» оставляет внутренние IPv4-адреса и имена пиров читаемыми для поддержки. Режим «Строгий» дополнительно анонимизирует частные (RFC 1918), CGNAT и link-local IP-адреса, имена пиров и публичные ключи WireGuard. Повторяющиеся значения заменяются одним и тем же заполнителем, поэтому пиры остаются различимыми. Используйте режим «Строгий», когда передаёте отладочный пакет за пределы вашей организации." + }, + "settings.troubleshooting.anonymize.none": { + "message": "Нет" + }, + "settings.troubleshooting.anonymize.default": { + "message": "По умолчанию" + }, + "settings.troubleshooting.anonymize.strict": { + "message": "Строгий" }, "settings.troubleshooting.systemInfo.label": { "message": "Включить сведения о системе" @@ -1338,5 +1350,14 @@ }, "error.unknown": { "message": "Не удалось выполнить операцию." + }, + "settings.ssh.privilege.hint": { + "message": "Требуются {actor}. Выполните вместо этого:" + }, + "settings.ssh.privilege.oneWay": { + "message": "Отключить можно, но чтобы включить снова, нужны {actor}:" + }, + "settings.ssh.privilege.oneWayInverted": { + "message": "Включить можно, но чтобы отключить снова, нужны {actor}:" } } diff --git a/client/ui/i18n/locales/zh-CN/common.json b/client/ui/i18n/locales/zh-CN/common.json index 542b2b045..90ae5e003 100644 --- a/client/ui/i18n/locales/zh-CN/common.json +++ b/client/ui/i18n/locales/zh-CN/common.json @@ -764,7 +764,19 @@ "message": "匿名化敏感信息" }, "settings.troubleshooting.anonymize.help": { - "message": "从日志中隐藏公共 IP 地址和非 NetBird 域名。" + "message": "隐藏 IP 地址、域名和其他敏感值。" + }, + "settings.troubleshooting.anonymize.info": { + "message": "默认级别保留内部 IPv4 地址和对等节点名称,便于支持人员阅读。严格级别还会匿名化私有 (RFC 1918)、CGNAT 和链路本地 IP 地址、对等节点名称以及 WireGuard 公钥。相同的值会映射到相同的占位符,因此对等节点仍可区分。向组织外部分享调试包时请使用严格级别。" + }, + "settings.troubleshooting.anonymize.none": { + "message": "无" + }, + "settings.troubleshooting.anonymize.default": { + "message": "默认" + }, + "settings.troubleshooting.anonymize.strict": { + "message": "严格" }, "settings.troubleshooting.systemInfo.label": { "message": "包含系统信息" @@ -1338,5 +1350,14 @@ }, "error.unknown": { "message": "操作失败。" + }, + "settings.ssh.privilege.hint": { + "message": "需要{actor}。请改为运行:" + }, + "settings.ssh.privilege.oneWay": { + "message": "您可以关闭此项,但重新开启需要{actor}:" + }, + "settings.ssh.privilege.oneWayInverted": { + "message": "您可以开启此项,但再次关闭需要{actor}:" } } diff --git a/client/wasm/cmd/main.go b/client/wasm/cmd/main.go index 4683f4033..260a528f0 100644 --- a/client/wasm/cmd/main.go +++ b/client/wasm/cmd/main.go @@ -56,8 +56,7 @@ func startClient(ctx context.Context, nbClient *netbird.Client) error { // parseClientOptions extracts NetBird options from JavaScript object func parseClientOptions(jsOptions js.Value) (netbird.Options, error) { options := netbird.Options{ - DeviceName: "dashboard-client", - LogLevel: defaultLogLevel, + LogLevel: defaultLogLevel, } if jwtToken := jsOptions.Get("jwtToken"); !jwtToken.IsNull() && !jwtToken.IsUndefined() { @@ -87,13 +86,41 @@ func parseClientOptions(jsOptions js.Value) (netbird.Options, error) { options.DeviceName = deviceName.String() } - if disableIPv6 := jsOptions.Get("disableIPv6"); !disableIPv6.IsNull() && !disableIPv6.IsUndefined() { - options.DisableIPv6 = disableIPv6.Bool() + disableIPv6, err := boolOption(jsOptions, "disableIPv6") + if err != nil { + return options, err + } + if disableIPv6 != nil { + options.DisableIPv6 = *disableIPv6 } + // The caller decides whether this client uses lazy connections; left unset it + // defers to the management feature flag. A short-lived, interactive caller + // turns it off so its sessions reach the few peers their grant covers eagerly, + // instead of the first request waiting for the connection to be established. + lazyConnectionEnabled, err := boolOption(jsOptions, "lazyConnectionEnabled") + if err != nil { + return options, err + } + options.LazyConnectionEnabled = lazyConnectionEnabled + return options, nil } +// boolOption reads a boolean option, returning nil when the caller left it out. +// js.Value.Bool panics on any other type, so a wrong type is reported instead. +func boolOption(jsOptions js.Value, name string) (*bool, error) { + v := jsOptions.Get(name) + if v.IsNull() || v.IsUndefined() { + return nil, nil + } + if v.Type() != js.TypeBoolean { + return nil, fmt.Errorf("option %s must be a boolean, got %s", name, v.Type()) + } + b := v.Bool() + return &b, nil +} + // createStartMethod creates the start method for the client func createStartMethod(client *netbird.Client) js.Func { return js.FuncOf(func(this js.Value, args []js.Value) any { diff --git a/client/wasm/cmd/main_test.go b/client/wasm/cmd/main_test.go new file mode 100644 index 000000000..3ec5a8f6a --- /dev/null +++ b/client/wasm/cmd/main_test.go @@ -0,0 +1,64 @@ +//go:build js + +package main + +import ( + "syscall/js" + "testing" +) + +// TestParseClientOptionsBooleans covers the boolean options against the value +// kinds a JS caller can pass: js.Value.Bool panics on anything but a boolean, +// so a wrong type has to be rejected before it reaches the client. +func TestParseClientOptionsBooleans(t *testing.T) { + t.Run("unset leaves the lazy override empty", func(t *testing.T) { + options, err := parseClientOptions(js.Global().Get("Object").New()) + if err != nil { + t.Fatalf("parse options: %v", err) + } + if options.LazyConnectionEnabled != nil { + t.Errorf("lazy override should stay unset, got %v", *options.LazyConnectionEnabled) + } + if options.DisableIPv6 { + t.Error("disableIPv6 should default to false") + } + }) + + t.Run("null defers to the management flag", func(t *testing.T) { + jsOptions := js.Global().Get("Object").New() + jsOptions.Set("lazyConnectionEnabled", js.Null()) + options, err := parseClientOptions(jsOptions) + if err != nil { + t.Fatalf("parse options: %v", err) + } + if options.LazyConnectionEnabled != nil { + t.Errorf("lazy override should stay unset, got %v", *options.LazyConnectionEnabled) + } + }) + + t.Run("booleans are carried through", func(t *testing.T) { + jsOptions := js.Global().Get("Object").New() + jsOptions.Set("lazyConnectionEnabled", false) + jsOptions.Set("disableIPv6", true) + options, err := parseClientOptions(jsOptions) + if err != nil { + t.Fatalf("parse options: %v", err) + } + if options.LazyConnectionEnabled == nil || *options.LazyConnectionEnabled { + t.Errorf("lazy override should be false, got %v", options.LazyConnectionEnabled) + } + if !options.DisableIPv6 { + t.Error("disableIPv6 should be true") + } + }) + + t.Run("a non-boolean is rejected", func(t *testing.T) { + for _, value := range []any{"true", 1, js.Global().Get("Object").New()} { + jsOptions := js.Global().Get("Object").New() + jsOptions.Set("lazyConnectionEnabled", value) + if _, err := parseClientOptions(jsOptions); err == nil { + t.Errorf("value %v should be rejected", value) + } + } + }) +} diff --git a/client/wasm/internal/ssh/client.go b/client/wasm/internal/ssh/client.go index 9cfe65266..28ae95ec0 100644 --- a/client/wasm/internal/ssh/client.go +++ b/client/wasm/internal/ssh/client.go @@ -80,13 +80,12 @@ func (c *Client) Connect(host string, port int, username, jwtToken string, ipVer return fmt.Errorf("dial %s: %w", addr, err) } - sshConn, chans, reqs, err := ssh.NewClientConn(conn, addr, config) + sshClient, err := nbssh.Handshake(ctx, conn, addr, config) if err != nil { - closeWithLog(conn, "connection after handshake error") - return fmt.Errorf("SSH handshake: %w", err) + return err } - c.sshClient = ssh.NewClient(sshConn, chans, reqs) + c.sshClient = sshClient logrus.Infof("SSH: Connected to %s", addr) return nil @@ -119,57 +118,26 @@ func (c *Client) getAuthMethods(jwtToken string) ([]ssh.AuthMethod, error) { return []ssh.AuthMethod{ssh.PublicKeys(signer)}, nil } -// StartSession starts an SSH session with PTY +// StartSession starts an SSH session with PTY. It holds the client lock for +// the whole startup so Close cannot tear the client down mid-setup and the +// new session cannot be installed into an already closed client. func (c *Client) StartSession(cols, rows int) error { + c.mu.Lock() + defer c.mu.Unlock() + if c.sshClient == nil { return fmt.Errorf("SSH client not connected") } - session, err := c.sshClient.NewSession() + pty, err := nbssh.StartPTYSession(c.sshClient, cols, rows) if err != nil { - return fmt.Errorf("create session: %w", err) + return err } - c.mu.Lock() - defer c.mu.Unlock() - c.session = session - - modes := ssh.TerminalModes{ - ssh.ECHO: 1, - ssh.TTY_OP_ISPEED: 14400, - ssh.TTY_OP_OSPEED: 14400, - ssh.VINTR: 3, - ssh.VQUIT: 28, - ssh.VERASE: 127, - } - - if err := session.RequestPty("xterm-256color", rows, cols, modes); err != nil { - closeWithLog(session, "session after PTY error") - return fmt.Errorf("PTY request: %w", err) - } - - c.stdin, err = session.StdinPipe() - if err != nil { - closeWithLog(session, "session after stdin error") - return fmt.Errorf("get stdin: %w", err) - } - - c.stdout, err = session.StdoutPipe() - if err != nil { - closeWithLog(session, "session after stdout error") - return fmt.Errorf("get stdout: %w", err) - } - - c.stderr, err = session.StderrPipe() - if err != nil { - closeWithLog(session, "session after stderr error") - return fmt.Errorf("get stderr: %w", err) - } - - if err := session.Shell(); err != nil { - closeWithLog(session, "session after shell error") - return fmt.Errorf("start shell: %w", err) - } + c.session = pty.Session + c.stdin = pty.Stdin + c.stdout = pty.Stdout + c.stderr = pty.Stderr logrus.Info("SSH: Session started with PTY") return nil diff --git a/flow/client/client.go b/flow/client/client.go index 3f31c2464..fc07db833 100644 --- a/flow/client/client.go +++ b/flow/client/client.go @@ -146,11 +146,14 @@ func (c *GRPCClient) Receive(ctx context.Context, interval time.Duration, msgHan streamStart := time.Now() - if err := c.receive(stream, msgHandler); err != nil { + // receive always returns a non-nil error once the stream breaks; + // handleRetryableError decides between reconnecting and exiting + // permanently on local context cancellation + err = c.receive(stream, msgHandler) + if !isContextDone(err) { log.Errorf("receive failed: %v", err) - return c.handleRetryableError(err, streamStart, backOff) } - return nil + return c.handleRetryableError(err, streamStart, backOff) } if err := backoff.Retry(operation, backOff); err != nil { diff --git a/go.mod b/go.mod index f119d4a92..265cd962f 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 github.com/vishvananda/netlink v1.3.1 - golang.org/x/crypto v0.54.0 + golang.org/x/crypto v0.55.0 golang.org/x/sys v0.47.0 golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 @@ -62,7 +62,6 @@ require ( github.com/goccy/go-yaml v1.18.0 github.com/godbus/dbus/v5 v5.2.2 github.com/golang-jwt/jwt/v5 v5.3.1 - github.com/golang/mock v1.6.0 github.com/google/go-cmp v0.7.0 github.com/google/gopacket v1.1.19 github.com/google/nftables v0.3.0 @@ -74,7 +73,6 @@ require ( github.com/hashicorp/go-version v1.7.0 github.com/jackc/pgx/v5 v5.5.5 github.com/libdns/route53 v1.5.0 - github.com/libp2p/go-nat v0.2.0 github.com/libp2p/go-netroute v0.4.0 github.com/lrh3321/ipset-go v0.0.0-20250619021614-54a0a98ace81 github.com/mdlayher/socket v0.5.1 @@ -82,6 +80,7 @@ require ( github.com/miekg/dns v1.1.72 github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/moby/moby/api v1.54.1 + github.com/netbirdio/go-nat v0.0.0-20260821095157-6b2c8c5c74e8 github.com/netbirdio/management-integrations/integrations v0.0.0-20260416123949-2355d972be42 github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20250805121659-6b4ac470ca45 github.com/oapi-codegen/runtime v1.1.2 @@ -127,9 +126,9 @@ require ( go.uber.org/zap v1.27.0 goauthentik.io/api/v3 v3.2023051.3 golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f - golang.org/x/mobile v0.0.0-20251113184115-a159579294ab - golang.org/x/mod v0.37.0 - golang.org/x/net v0.56.0 + golang.org/x/mobile v0.0.0-20260816165457-f98cc9b3c733 + golang.org/x/mod v0.39.0 + golang.org/x/net v0.58.0 golang.org/x/oauth2 v0.36.0 golang.org/x/sync v0.22.0 golang.org/x/term v0.45.0 @@ -217,6 +216,7 @@ require ( github.com/gobwas/pool v0.2.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect + github.com/golang/mock v1.6.0 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/go-tpm v0.9.8 // indirect @@ -313,8 +313,8 @@ require ( go.opentelemetry.io/otel/trace v1.43.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect - golang.org/x/text v0.40.0 // indirect - golang.org/x/tools v0.47.0 // indirect + golang.org/x/text v0.41.0 // indirect + golang.org/x/tools v0.49.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect @@ -340,3 +340,5 @@ replace github.com/dexidp/dex/api/v2 => github.com/netbirdio/dex/api/v2 v2.0.0-2 replace github.com/mailru/easyjson => github.com/netbirdio/easyjson v0.9.0 replace github.com/wailsapp/wails/v3 => github.com/netbirdio/wails/v3 v3.0.0-beta.3.0.20260810103952-24e716aea4db + +tool go.uber.org/mock/mockgen diff --git a/go.sum b/go.sum index 31e8b5454..d9d880ede 100644 --- a/go.sum +++ b/go.sum @@ -407,8 +407,6 @@ github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s= github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/libdns/route53 v1.5.0 h1:2SKdpPFl/qgWsXQvsLNJJAoX7rSxlk7zgoL4jnWdXVA= github.com/libdns/route53 v1.5.0/go.mod h1:joT4hKmaTNKHEwb7GmZ65eoDz1whTu7KKYPS8ZqIh6Q= -github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk= -github.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk= github.com/libp2p/go-netroute v0.4.0 h1:sZZx9hyANYUx9PZyqcgE/E1GUG3iEtTZHUEvdtXT7/Q= github.com/libp2p/go-netroute v0.4.0/go.mod h1:Nkd5ShYgSMS5MUKy/MU2T57xFoOKvvLR92Lic48LEyA= github.com/lrh3321/ipset-go v0.0.0-20250619021614-54a0a98ace81 h1:J56rFEfUTFT9j9CiRXhi1r8lUJ4W5idG3CiaBZGojNU= @@ -480,6 +478,8 @@ github.com/netbirdio/dex/api/v2 v2.0.0-20260512110716-8d70ad8647c1 h1:neE7z+FPUk github.com/netbirdio/dex/api/v2 v2.0.0-20260512110716-8d70ad8647c1/go.mod h1:awuTyT29CYALpEyET0S307EgNlPWrc7fFKRAyhsO45M= github.com/netbirdio/easyjson v0.9.0 h1:6Nw2lghSVuy8RSkAYDhDv1thBVEmfVbKZnV7T7Z6Aus= github.com/netbirdio/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/netbirdio/go-nat v0.0.0-20260821095157-6b2c8c5c74e8 h1:pBxXEsxcsO3qVUND//5j1kelYlO57x5IrRviNF0+0iA= +github.com/netbirdio/go-nat v0.0.0-20260821095157-6b2c8c5c74e8/go.mod h1:mFViabv4PpnoDw9w7W21a7xux6APA4q7KQZRsv4BCl8= github.com/netbirdio/ice/v4 v4.0.0-20250908184934-6202be846b51 h1:Ov4qdafATOgGMB1wbSuh+0aAHcwz9hdvB6VZjh1mVMI= github.com/netbirdio/ice/v4 v4.0.0-20250908184934-6202be846b51/go.mod h1:ZSIbPdBn5hePO8CpF1PekH2SfpTxg1PDhEwtbqZS7R8= github.com/netbirdio/management-integrations/integrations v0.0.0-20260416123949-2355d972be42 h1:F3zS5fT9xzD1OFLfcdAE+3FfyiwjGukF1hvj0jErgs8= @@ -728,13 +728,13 @@ golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1m golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= -golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= +golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M= +golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis= golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM= golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20251113184115-a159579294ab h1:Iqyc+2zr7aGyLuEadIm0KRJP0Wwt+fhlXLa51Fxf1+Q= -golang.org/x/mobile v0.0.0-20251113184115-a159579294ab/go.mod h1:Eq3Nh/5pFSWug2ohiudJ1iyU59SO78QFuh4qTTN++I0= +golang.org/x/mobile v0.0.0-20260816165457-f98cc9b3c733 h1:XKMObIaAElmkdO+4SQh1iCfzwciZHJi1OblnX9BED9k= +golang.org/x/mobile v0.0.0-20260816165457-f98cc9b3c733/go.mod h1:jMwjxoDSx9jqhNaZqPnr6nnKzb7cs+Dy1Czk7wdX+R8= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -744,8 +744,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= -golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= +golang.org/x/mod v0.39.0 h1:UF5zwQdCRRUpHfyPwr7d4UrGiVeldIsogtzWVnczL74= +golang.org/x/mod v0.39.0/go.mod h1:bvIbwjQ0HUFFf5AKukeeYQG4ZBUG9yxQbR9aEweIwYY= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= @@ -764,8 +764,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= -golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= +golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= +golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= @@ -843,8 +843,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= -golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= +golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= +golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -858,8 +858,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= -golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= +golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI= +golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/infrastructure_files/migrate-to-enterprise.sh b/infrastructure_files/migrate-to-enterprise.sh index 5f69b4a90..744ba5375 100755 --- a/infrastructure_files/migrate-to-enterprise.sh +++ b/infrastructure_files/migrate-to-enterprise.sh @@ -15,6 +15,12 @@ set -o pipefail # 2. Postgres migration — add Postgres, migrate SQLite data via migrate-store. # 3. Traffic flow — add NATS + flow-enricher + flow-receiver. # +# Step 2 is skipped when the deployment already runs on Postgres +# (server.store.engine: postgres in config.yaml). Nothing is provisioned or +# migrated in that case and the store config is left exactly as the operator +# wrote it — the enterprise image reads the same Postgres the community image +# did. Such a deployment gets the image swap, and can still opt into step 3. +# # If any step fails once the stack has been touched, the script rolls itself # back automatically: generated files are removed, the Postgres volume this run # created is dropped, and the original deployment is started again. @@ -38,6 +44,18 @@ ENV_BACKUP="" PG_VOLUME_NAME="" BACKUP_DIR="" +# Store state. STORE_ENGINE is what the deployment runs on today; when it is +# already postgres, MIGRATE_POSTGRES stays "no" and nothing is provisioned. +# POSTGRES_SERVICE is empty when Postgres lives outside this compose project. +STORE_ENGINE="" +EXISTING_POSTGRES="no" +POSTGRES_DSN="" +POSTGRES_SERVICE="" +POSTGRES_DEPENDS_CONDITION="service_healthy" +# Whether this run needs to generate config.yaml.enterprise at all. A pure +# image swap does not. +ENTERPRISE_CONFIG="no" + NETBIRD_EULA_URL="https://netbird.io/self-hosted-EULA" check_docker_compose() { @@ -192,6 +210,85 @@ detect_exposed_address() { yq eval '.server.exposedAddress // ""' "$CONFIG_YAML_HOST" } +# The engine is a config.yaml-only setting — there is no env override for it +# (combined/cmd/root.go reads it from YAML and derives the env vars), so +# config.yaml is authoritative. Absent means the sqlite default. +detect_store_engine() { + local engine + engine=$(yq eval '.server.store.engine // ""' "$CONFIG_YAML_HOST") + if [[ -z "$engine" ]] || [[ "$engine" == "null" ]]; then + engine="sqlite" + fi + echo "$engine" | tr '[:upper:]' '[:lower:]' +} + +detect_store_dsn() { + yq eval '.server.store.dsn // ""' "$CONFIG_YAML_HOST" +} + +# config.yaml is where a combined deployment carries its DSN; this only covers +# hand-rolled installs that keep it in the environment instead. +detect_store_dsn_from_compose() { + # `compose config` re-escapes a literal $ as $$ on the way out, so undo that + # to get the value the container actually receives. + $DOCKER_COMPOSE_COMMAND config 2>/dev/null | yq eval " + .services[\"$COMBINED_SERVICE\"].environment.NB_STORE_ENGINE_POSTGRES_DSN // + .services[\"$COMBINED_SERVICE\"].environment.NETBIRD_STORE_ENGINE_POSTGRES_DSN // \"\" + " - 2>/dev/null | sed 's/\$\$/$/g' +} + +# Reads either DSN form: "host=db ..." or "postgres://user:pass@db:5432/name". +dsn_host() { + local dsn="$1" + case "$dsn" in + *://*) printf '%s' "$dsn" | sed -n 's,^[a-zA-Z+]*://\([^/?]*\).*,\1,p' | sed -e 's,.*@,,' -e 's,:.*,,' ;; + *) printf '%s' "$dsn" | sed -n 's/.*[[:space:]]*host=\([^[:space:]]*\).*/\1/p' ;; + esac +} + +# flow-enricher is its own container, so a loopback host or a socket path would +# reach the enricher rather than Postgres. Only flag hosts we can positively +# identify — an unparseable DSN must not leave the operator with no way forward. +dsn_host_reachable() { + local dsn="$1" + case "$(dsn_host "$dsn")" in + localhost | 127.* | ::1 | 0.0.0.0 | /*) return 1 ;; + *) return 0 ;; + esac +} + +# Names the compose service running this deployment's Postgres, for depends_on. +# Empty means external — the DSN host matched no service. A DSN with no readable +# host falls back to matching on image. +detect_postgres_service() { + local host + host=$(dsn_host "$POSTGRES_DSN") + if [[ -n "$host" ]]; then + if [[ "$(host="$host" yq eval '.services | has(env(host))' "$COMPOSE_FILE" 2>/dev/null)" == "true" ]]; then + echo "$host" + fi + return + fi + yq eval '.services | to_entries | map(select(.value.image // "" | test("(^|/)(postgres|postgis|pgvector|timescaledb)(:|@|$)"))) | .[0].key // ""' "$COMPOSE_FILE" +} + +# depends_on: service_healthy is only legal if the service defines a healthcheck. +detect_postgres_depends_condition() { + local tag + tag=$(yq eval ".services[\"$POSTGRES_SERVICE\"].healthcheck | tag" "$COMPOSE_FILE" 2>/dev/null) + if [[ "$tag" == "!!map" ]]; then + echo "service_healthy" + else + echo "service_started" + fi +} + +env_value() { + local value="$1" + value=$(printf '%s' "$value" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/\$/$$/g') + printf '"%s"' "$value" +} + detect_compose_network() { local tag tag=$(yq eval ".services[\"$COMBINED_SERVICE\"].networks | tag" "$COMPOSE_FILE" 2>/dev/null) @@ -221,9 +318,6 @@ render_override() { # Remove this file (and config.yaml.enterprise if present) to revert. services: - ${DASHBOARD_SERVICE}: - image: \${NETBIRD_DASHBOARD_IMAGE:-ghcr.io/netbirdio/dashboard-cloud:latest} - ${COMBINED_SERVICE}: image: \${NETBIRD_SERVER_IMAGE:-ghcr.io/netbirdio/netbird-server-cloud:latest} environment: @@ -231,16 +325,30 @@ services: NETBIRD_LICENSE_SERVER_BASE_URL: \${NETBIRD_LICENSE_SERVER_BASE_URL} EOF + # An existing Postgres is already wired up by the operator's own compose file, + # so only a Postgres this run creates needs a depends_on. if [[ "$MIGRATE_POSTGRES" == "yes" ]]; then cat < "$ENTERPRISE_CONFIG_FILE" - yq eval " - .server.store.engine = \"postgres\" | - .server.store.dsn = \"$pg_dsn\" | - .server.activityStore.engine = \"postgres\" | - .server.activityStore.dsn = \"$pg_dsn\" | - .server.authStore.engine = \"postgres\" | - .server.authStore.dsn = \"$pg_dsn\" - " "$CONFIG_YAML_HOST" > "$ENTERPRISE_CONFIG_FILE" + if [[ "$MIGRATE_POSTGRES" == "yes" ]]; then + # Fresh Postgres: point every store section at it. migrate-store carries the + # SQLite contents across. + POSTGRES_DSN="$POSTGRES_DSN" yq eval -i ' + .server.store.engine = "postgres" | + .server.store.dsn = strenv(POSTGRES_DSN) | + .server.activityStore.engine = "postgres" | + .server.activityStore.dsn = strenv(POSTGRES_DSN) | + .server.authStore.engine = "postgres" | + .server.authStore.dsn = strenv(POSTGRES_DSN) + ' "$ENTERPRISE_CONFIG_FILE" + fi + # Otherwise the store config is the operator's and stays untouched. + # activityStore and authStore do not inherit from server.store — each falls + # back to its own SQLite file under dataDir — so repointing them at Postgres + # here would silently strand the existing audit log and the embedded IdP's + # users, with no migrate-store run to carry them over. if [[ "$ENABLE_FLOW" == "yes" ]]; then - local flow_addr="${NETBIRD_DOMAIN}" - yq eval -i " + NETBIRD_DOMAIN="$NETBIRD_DOMAIN" yq eval -i ' .server.trafficFlow.enabled = true | - .server.trafficFlow.address = \"$flow_addr\" | - .server.trafficFlow.interval = \"60s\" - " "$ENTERPRISE_CONFIG_FILE" + .server.trafficFlow.address = strenv(NETBIRD_DOMAIN) | + .server.trafficFlow.interval = "60s" + ' "$ENTERPRISE_CONFIG_FILE" fi } @@ -633,6 +761,91 @@ on_exit() { # Main # --------------------------------------------------------------------------- +# Already on Postgres: there is nothing to provision and nothing to migrate. +# The enterprise image reads the very same store config the community image +# did, so step 2 collapses to a no-op and the run is a plain image swap. +configure_existing_postgres() { + EXISTING_POSTGRES="yes" + MIGRATE_POSTGRES="no" + + # DSN first — detect_postgres_service prefers the host it names. + POSTGRES_DSN=$(detect_store_dsn) + if [[ -z "$POSTGRES_DSN" ]] || [[ "$POSTGRES_DSN" == "null" ]]; then + POSTGRES_DSN=$(detect_store_dsn_from_compose) + fi + if [[ "$POSTGRES_DSN" == "null" ]]; then + POSTGRES_DSN="" + fi + + POSTGRES_SERVICE=$(detect_postgres_service) + if [[ -n "$POSTGRES_SERVICE" ]]; then + POSTGRES_DEPENDS_CONDITION=$(detect_postgres_depends_condition) + fi + + echo "Step 2: Postgres migration not needed — this deployment already runs on" + echo " Postgres. Its store configuration is reused as-is and left" + echo " untouched; no database is created and no data is moved." + if [[ -n "$POSTGRES_SERVICE" ]]; then + echo " Postgres service: $POSTGRES_SERVICE (in $COMPOSE_FILE)" + else + echo " Postgres service: managed outside $COMPOSE_FILE" + fi +} + +configure_sqlite_store() { + MIGRATE_POSTGRES=$(read_yes_no "Step 2: Migrate storage from SQLite to Postgres? (recommended)" "n") + [[ "$MIGRATE_POSTGRES" == "yes" ]] || return 0 + + # The override would otherwise merge into a service of the same name and + # quietly rewrite its image and credentials. + local existing + existing=$(yq eval '.services | has("postgres")' "$COMPOSE_FILE") + if [[ "$existing" == "true" ]]; then + echo "" > /dev/stderr + echo "$COMPOSE_FILE already defines a service named 'postgres', but config.yaml" > /dev/stderr + echo "still has server.store.engine: sqlite. This script would add its own" > /dev/stderr + echo "'postgres' service and Compose would merge the two." > /dev/stderr + echo "" > /dev/stderr + echo "Point server.store.engine at that Postgres yourself, or rename the service," > /dev/stderr + echo "then re-run." > /dev/stderr + exit 1 + fi + + echo "" + echo " ⚠ Data will be migrated from SQLite to Postgres. The SQLite store" + echo " will be backed up automatically. To fully revert later, restore" + echo " that backup and delete docker-compose.override.yml +" + echo " config.yaml.enterprise." + local confirm + confirm=$(read_yes_no " Continue?" "y") + if [[ "$confirm" != "yes" ]]; then + MIGRATE_POSTGRES="no" + echo " Skipping Postgres migration." + return 0 + fi + + POSTGRES_PASSWORD=$(rand_password) + POSTGRES_SERVICE="postgres" + POSTGRES_DEPENDS_CONDITION="service_healthy" + POSTGRES_DSN="host=postgres user=netbird password=${POSTGRES_PASSWORD} dbname=netbird port=5432 sslmode=disable" +} + +# mysql, or something this script has never seen. Swapping the images is still +# valid; touching the store is not. +configure_unsupported_store() { + MIGRATE_POSTGRES="no" + echo " ⚠ server.store.engine is '$STORE_ENGINE'. This script only migrates" + echo " SQLite to Postgres, and traffic flow requires Postgres, so both are" + echo " unavailable here. The store configuration will be left untouched." + echo "" + local proceed + proceed=$(read_yes_no "Step 2 skipped. Continue with the image swap only?" "n") + if [[ "$proceed" != "yes" ]]; then + echo "Aborted." + exit 0 + fi +} + init_migration() { DOCKER_COMPOSE_COMMAND=$(check_docker_compose) check_yq @@ -682,12 +895,15 @@ init_migration() { exit 1 fi + STORE_ENGINE=$(detect_store_engine) + echo "Detected existing deployment:" echo " Combined service: $COMBINED_SERVICE" echo " Dashboard: $DASHBOARD_SERVICE" echo " config.yaml: $CONFIG_YAML_HOST" echo " Data volume: $DATA_VOLUME" echo " Network: $COMPOSE_NETWORK" + echo " Store engine: $STORE_ENGINE" echo "" require_eula_acceptance @@ -706,28 +922,17 @@ init_migration() { echo "Step 1: Image swap (community → Enterprise). License key required." NB_LICENSE_KEY=$(read_secret " License key") - # Step 2 — optional + # Step 2 — what this does depends on what the deployment already stores in. echo "" - MIGRATE_POSTGRES=$(read_yes_no "Step 2: Migrate storage from SQLite to Postgres? (recommended)" "n") - if [[ "$MIGRATE_POSTGRES" == "yes" ]]; then - echo "" - echo " ⚠ Data will be migrated from SQLite to Postgres. The SQLite store" - echo " will be backed up automatically. To fully revert later, restore" - echo " that backup and delete docker-compose.override.yml +" - echo " config.yaml.enterprise." - local confirm - confirm=$(read_yes_no " Continue?" "y") - if [[ "$confirm" != "yes" ]]; then - MIGRATE_POSTGRES="no" - echo " Skipping Postgres migration." - else - POSTGRES_PASSWORD=$(rand_password) - fi - fi + case "$STORE_ENGINE" in + postgres) configure_existing_postgres ;; + sqlite) configure_sqlite_store ;; + *) configure_unsupported_store ;; + esac # Step 3 — optional, only if Postgres is on (flow requires Postgres) echo "" - if [[ "$MIGRATE_POSTGRES" == "yes" ]]; then + if [[ "$MIGRATE_POSTGRES" == "yes" ]] || [[ "$EXISTING_POSTGRES" == "yes" ]]; then ENABLE_FLOW=$(read_yes_no "Step 3: Enable traffic flow? (requires Postgres)" "n") if [[ "$ENABLE_FLOW" == "yes" ]]; then # Auth secret MUST match server.authSecret from config.yaml @@ -751,12 +956,46 @@ init_migration() { echo "Could not read server.store.encryptionKey from $CONFIG_YAML_HOST." > /dev/stderr exit 1 fi + + # flow-enricher talks to Postgres directly, so this is the one place an + # existing deployment's DSN is actually needed — and the one place a host + # that only works from inside the server container shows up. + while :; do + local dsn_problem="" + if [[ -z "$POSTGRES_DSN" ]]; then + dsn_problem="No DSN could be read from $CONFIG_YAML_HOST or from the $COMBINED_SERVICE environment." + elif ! dsn_host_reachable "$POSTGRES_DSN"; then + dsn_problem="Its host '$(dsn_host "$POSTGRES_DSN")' only resolves inside the server container." + fi + [[ -n "$dsn_problem" ]] || break + + echo "" + echo " The flow enricher reaches Postgres from a container of its own." + echo " $dsn_problem" + echo " Enter a DSN reachable from other containers, or press Ctrl-C to abort." + POSTGRES_DSN=$(read_required " Postgres DSN (host=… user=… password=… dbname=… port=5432 sslmode=disable)") + done + + # Only where the operator owns Postgres: a DSN entered above may name a + # different host. The sqlite path creates its own service, nothing to find. + if [[ "$EXISTING_POSTGRES" == "yes" ]]; then + POSTGRES_SERVICE=$(detect_postgres_service) + if [[ -n "$POSTGRES_SERVICE" ]]; then + POSTGRES_DEPENDS_CONDITION=$(detect_postgres_depends_condition) + fi + fi fi else ENABLE_FLOW="no" echo "Step 3 (traffic flow) skipped — requires Postgres." fi + # config.yaml.enterprise only exists to hold changes; without any there is + # nothing to generate and the server keeps running on its own config.yaml. + if [[ "$MIGRATE_POSTGRES" == "yes" ]] || [[ "$ENABLE_FLOW" == "yes" ]]; then + ENTERPRISE_CONFIG="yes" + fi + check_data_directory check_stale_postgres_volume } @@ -774,7 +1013,7 @@ apply_changes() { sed -i.bak '/NETBIRD_LICENSE_SERVER_BASE_URL/d' "$OVERRIDE_FILE" && rm -f "$OVERRIDE_FILE.bak" fi - if [[ "$MIGRATE_POSTGRES" == "yes" ]]; then + if [[ "$ENTERPRISE_CONFIG" == "yes" ]]; then echo "Writing $ENTERPRISE_CONFIG_FILE ..." install -m 600 /dev/null "$ENTERPRISE_CONFIG_FILE" render_enterprise_config @@ -810,6 +1049,9 @@ apply_changes() { echo "POSTGRES_PASSWORD=${POSTGRES_PASSWORD}" fi if [[ "$ENABLE_FLOW" == "yes" ]]; then + # Own variable name rather than NB_STORE_ENGINE_POSTGRES_DSN so that a + # deployment already setting that one keeps its own value. + echo "NB_ENTERPRISE_POSTGRES_DSN=$(env_value "$POSTGRES_DSN")" echo "NB_FLOW_AUTH_SECRET=${NB_FLOW_AUTH_SECRET}" echo "NETBIRD_ENCRYPTION_KEY=${NETBIRD_ENCRYPTION_KEY}" fi @@ -871,14 +1113,19 @@ print_summary() { echo " Summary" echo "──────────────────────────────────────────────────────────────────────" echo " Images: swapped to enterprise" - [[ "$MIGRATE_POSTGRES" == "yes" ]] && echo " Storage: Postgres (data migrated from SQLite)" - [[ "$MIGRATE_POSTGRES" != "yes" ]] && echo " Storage: SQLite (unchanged)" + if [[ "$MIGRATE_POSTGRES" == "yes" ]]; then + echo " Storage: Postgres (data migrated from SQLite)" + elif [[ "$EXISTING_POSTGRES" == "yes" ]]; then + echo " Storage: Postgres (pre-existing, configuration unchanged)" + else + echo " Storage: $STORE_ENGINE (unchanged)" + fi [[ "$ENABLE_FLOW" == "yes" ]] && echo " Traffic flow: enabled" [[ "$ENABLE_FLOW" != "yes" ]] && echo " Traffic flow: disabled" echo "" echo " Generated files (next to your docker-compose.yml):" echo " $OVERRIDE_FILE" - [[ "$MIGRATE_POSTGRES" == "yes" ]] && echo " $ENTERPRISE_CONFIG_FILE" + [[ "$ENTERPRISE_CONFIG" == "yes" ]] && echo " $ENTERPRISE_CONFIG_FILE" echo " .env (license key + secrets, mode 600)" [[ "$ENV_EXISTED" == "yes" ]] && [[ -f "$ENV_BACKUP" ]] && echo " $ENV_BACKUP (.env as it was before this run)" [[ "$MIGRATE_POSTGRES" == "yes" ]] && echo " backups/sqlite-pre-enterprise-*/ (SQLite backup)" @@ -902,7 +1149,11 @@ print_summary() { else echo " $DOCKER_COMPOSE_COMMAND down" fi - echo " rm -f $OVERRIDE_FILE $ENTERPRISE_CONFIG_FILE" + if [[ "$ENTERPRISE_CONFIG" == "yes" ]]; then + echo " rm -f $OVERRIDE_FILE $ENTERPRISE_CONFIG_FILE" + else + echo " rm -f $OVERRIDE_FILE" + fi if [[ "$ENV_EXISTED" == "yes" ]] && [[ -f "$ENV_BACKUP" ]]; then echo " mv $ENV_BACKUP .env # restores .env as it was before this run" elif [[ "$ENV_EXISTED" == "no" ]]; then diff --git a/management/internals/controllers/network_map/controller/controller.go b/management/internals/controllers/network_map/controller/controller.go index 356dc9f67..07f1938c5 100644 --- a/management/internals/controllers/network_map/controller/controller.go +++ b/management/internals/controllers/network_map/controller/controller.go @@ -1024,7 +1024,7 @@ func (c *Controller) OnPeersDeleted(ctx context.Context, accountID string, peerI FirewallRules: []*proto.FirewallRule{}, FirewallRulesIsEmpty: true, DNSConfig: &proto.DNSConfig{ - ForwarderPort: dnsFwdPort, + ForwarderPort: dnsFwdPort, //nolint:staticcheck }, }, }, diff --git a/management/internals/controllers/network_map/interface.go b/management/internals/controllers/network_map/interface.go index e6e464566..b535321d1 100644 --- a/management/internals/controllers/network_map/interface.go +++ b/management/internals/controllers/network_map/interface.go @@ -1,6 +1,6 @@ package network_map -//go:generate go run go.uber.org/mock/mockgen -package network_map -destination=interface_mock.go -source=./interface.go -build_flags=-mod=mod +//go:generate go tool mockgen -package network_map -destination=interface_mock.go -source=./interface.go -build_flags=-mod=mod import ( "context" diff --git a/management/internals/modules/agentnetwork/handlers/handlers_test.go b/management/internals/modules/agentnetwork/handlers/handlers_test.go index 9d855c05d..6d1be3562 100644 --- a/management/internals/modules/agentnetwork/handlers/handlers_test.go +++ b/management/internals/modules/agentnetwork/handlers/handlers_test.go @@ -10,7 +10,7 @@ import ( "strings" "testing" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/management/internals/modules/agentnetwork/policyselect_model_test.go b/management/internals/modules/agentnetwork/policyselect_model_test.go index c122cc36c..7ae13e4ef 100644 --- a/management/internals/modules/agentnetwork/policyselect_model_test.go +++ b/management/internals/modules/agentnetwork/policyselect_model_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/management/internals/modules/agentnetwork/policyselect_test.go b/management/internals/modules/agentnetwork/policyselect_test.go index dd7687fe1..9ca548344 100644 --- a/management/internals/modules/agentnetwork/policyselect_test.go +++ b/management/internals/modules/agentnetwork/policyselect_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/management/internals/modules/agentnetwork/reconcile_test.go b/management/internals/modules/agentnetwork/reconcile_test.go index cda3a9549..ab3b08481 100644 --- a/management/internals/modules/agentnetwork/reconcile_test.go +++ b/management/internals/modules/agentnetwork/reconcile_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/management/internals/modules/agentnetwork/settings_bootstrap_test.go b/management/internals/modules/agentnetwork/settings_bootstrap_test.go index 0389ed4f5..fc6fd8b82 100644 --- a/management/internals/modules/agentnetwork/settings_bootstrap_test.go +++ b/management/internals/modules/agentnetwork/settings_bootstrap_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/management/internals/modules/agentnetwork/synthesizer_test.go b/management/internals/modules/agentnetwork/synthesizer_test.go index 387f44b74..817129571 100644 --- a/management/internals/modules/agentnetwork/synthesizer_test.go +++ b/management/internals/modules/agentnetwork/synthesizer_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/management/internals/modules/agentnetwork/wire_shape_test.go b/management/internals/modules/agentnetwork/wire_shape_test.go index 779dd77f9..c8877731e 100644 --- a/management/internals/modules/agentnetwork/wire_shape_test.go +++ b/management/internals/modules/agentnetwork/wire_shape_test.go @@ -5,7 +5,7 @@ import ( "encoding/json" "testing" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/management/internals/modules/peers/ephemeral/manager/ephemeral_test.go b/management/internals/modules/peers/ephemeral/manager/ephemeral_test.go index 314e84501..1b64c447a 100644 --- a/management/internals/modules/peers/ephemeral/manager/ephemeral_test.go +++ b/management/internals/modules/peers/ephemeral/manager/ephemeral_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" diff --git a/management/internals/modules/peers/manager.go b/management/internals/modules/peers/manager.go index 6f292f6ed..3274ec524 100644 --- a/management/internals/modules/peers/manager.go +++ b/management/internals/modules/peers/manager.go @@ -1,6 +1,6 @@ package peers -//go:generate go run github.com/golang/mock/mockgen -package peers -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod +//go:generate go tool mockgen -package peers -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod import ( "context" diff --git a/management/internals/modules/peers/manager_mock.go b/management/internals/modules/peers/manager_mock.go index 3836ac909..8c26d43b1 100644 --- a/management/internals/modules/peers/manager_mock.go +++ b/management/internals/modules/peers/manager_mock.go @@ -1,5 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: ./manager.go +// +// Generated by this command: +// +// mockgen -package peers -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod +// // Package peers is a generated GoMock package. package peers @@ -9,18 +14,19 @@ import ( net "net" reflect "reflect" - gomock "github.com/golang/mock/gomock" network_map "github.com/netbirdio/netbird/management/internals/controllers/network_map" account "github.com/netbirdio/netbird/management/server/account" integrated_validator "github.com/netbirdio/netbird/management/server/integrations/integrated_validator" peer "github.com/netbirdio/netbird/management/server/peer" types "github.com/netbirdio/netbird/management/server/types" + gomock "go.uber.org/mock/gomock" ) // MockManager is a mock of Manager interface. type MockManager struct { ctrl *gomock.Controller recorder *MockManagerMockRecorder + isgomock struct{} } // MockManagerMockRecorder is the mock recorder for MockManager. @@ -49,7 +55,7 @@ func (m *MockManager) CreateProxyPeer(ctx context.Context, accountID, peerKey, c } // CreateProxyPeer indicates an expected call of CreateProxyPeer. -func (mr *MockManagerMockRecorder) CreateProxyPeer(ctx, accountID, peerKey, cluster interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) CreateProxyPeer(ctx, accountID, peerKey, cluster any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateProxyPeer", reflect.TypeOf((*MockManager)(nil).CreateProxyPeer), ctx, accountID, peerKey, cluster) } @@ -63,7 +69,7 @@ func (m *MockManager) DeletePeers(ctx context.Context, accountID string, peerIDs } // DeletePeers indicates an expected call of DeletePeers. -func (mr *MockManagerMockRecorder) DeletePeers(ctx, accountID, peerIDs, userID, checkConnected interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) DeletePeers(ctx, accountID, peerIDs, userID, checkConnected any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePeers", reflect.TypeOf((*MockManager)(nil).DeletePeers), ctx, accountID, peerIDs, userID, checkConnected) } @@ -78,7 +84,7 @@ func (m *MockManager) GetAllPeers(ctx context.Context, accountID, userID string) } // GetAllPeers indicates an expected call of GetAllPeers. -func (mr *MockManagerMockRecorder) GetAllPeers(ctx, accountID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetAllPeers(ctx, accountID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllPeers", reflect.TypeOf((*MockManager)(nil).GetAllPeers), ctx, accountID, userID) } @@ -93,7 +99,7 @@ func (m *MockManager) GetPeer(ctx context.Context, accountID, userID, peerID str } // GetPeer indicates an expected call of GetPeer. -func (mr *MockManagerMockRecorder) GetPeer(ctx, accountID, userID, peerID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetPeer(ctx, accountID, userID, peerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeer", reflect.TypeOf((*MockManager)(nil).GetPeer), ctx, accountID, userID, peerID) } @@ -108,7 +114,7 @@ func (m *MockManager) GetPeerAccountID(ctx context.Context, peerID string) (stri } // GetPeerAccountID indicates an expected call of GetPeerAccountID. -func (mr *MockManagerMockRecorder) GetPeerAccountID(ctx, peerID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetPeerAccountID(ctx, peerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerAccountID", reflect.TypeOf((*MockManager)(nil).GetPeerAccountID), ctx, peerID) } @@ -123,7 +129,7 @@ func (m *MockManager) GetPeerByTunnelIP(ctx context.Context, accountID string, i } // GetPeerByTunnelIP indicates an expected call of GetPeerByTunnelIP. -func (mr *MockManagerMockRecorder) GetPeerByTunnelIP(ctx, accountID, ip interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetPeerByTunnelIP(ctx, accountID, ip any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerByTunnelIP", reflect.TypeOf((*MockManager)(nil).GetPeerByTunnelIP), ctx, accountID, ip) } @@ -138,7 +144,7 @@ func (m *MockManager) GetPeerID(ctx context.Context, peerKey string) (string, er } // GetPeerID indicates an expected call of GetPeerID. -func (mr *MockManagerMockRecorder) GetPeerID(ctx, peerKey interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetPeerID(ctx, peerKey any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerID", reflect.TypeOf((*MockManager)(nil).GetPeerID), ctx, peerKey) } @@ -154,7 +160,7 @@ func (m *MockManager) GetPeerWithGroups(ctx context.Context, accountID, peerID s } // GetPeerWithGroups indicates an expected call of GetPeerWithGroups. -func (mr *MockManagerMockRecorder) GetPeerWithGroups(ctx, accountID, peerID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetPeerWithGroups(ctx, accountID, peerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerWithGroups", reflect.TypeOf((*MockManager)(nil).GetPeerWithGroups), ctx, accountID, peerID) } @@ -169,7 +175,7 @@ func (m *MockManager) GetPeersByGroupIDs(ctx context.Context, accountID string, } // GetPeersByGroupIDs indicates an expected call of GetPeersByGroupIDs. -func (mr *MockManagerMockRecorder) GetPeersByGroupIDs(ctx, accountID, groupsIDs interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetPeersByGroupIDs(ctx, accountID, groupsIDs any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeersByGroupIDs", reflect.TypeOf((*MockManager)(nil).GetPeersByGroupIDs), ctx, accountID, groupsIDs) } @@ -181,7 +187,7 @@ func (m *MockManager) SetAccountManager(accountManager account.Manager) { } // SetAccountManager indicates an expected call of SetAccountManager. -func (mr *MockManagerMockRecorder) SetAccountManager(accountManager interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) SetAccountManager(accountManager any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetAccountManager", reflect.TypeOf((*MockManager)(nil).SetAccountManager), accountManager) } @@ -193,7 +199,7 @@ func (m *MockManager) SetIntegratedPeerValidator(integratedPeerValidator integra } // SetIntegratedPeerValidator indicates an expected call of SetIntegratedPeerValidator. -func (mr *MockManagerMockRecorder) SetIntegratedPeerValidator(integratedPeerValidator interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) SetIntegratedPeerValidator(integratedPeerValidator any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetIntegratedPeerValidator", reflect.TypeOf((*MockManager)(nil).SetIntegratedPeerValidator), integratedPeerValidator) } @@ -205,7 +211,7 @@ func (m *MockManager) SetNetworkMapController(networkMapController network_map.C } // SetNetworkMapController indicates an expected call of SetNetworkMapController. -func (mr *MockManagerMockRecorder) SetNetworkMapController(networkMapController interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) SetNetworkMapController(networkMapController any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetNetworkMapController", reflect.TypeOf((*MockManager)(nil).SetNetworkMapController), networkMapController) } diff --git a/management/internals/modules/reverseproxy/accesslogs/manager/manager_test.go b/management/internals/modules/reverseproxy/accesslogs/manager/manager_test.go index 11bf60829..8e941d7e5 100644 --- a/management/internals/modules/reverseproxy/accesslogs/manager/manager_test.go +++ b/management/internals/modules/reverseproxy/accesslogs/manager/manager_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/management/internals/modules/reverseproxy/proxy/manager.go b/management/internals/modules/reverseproxy/proxy/manager.go index 22f1007ec..26214c11b 100644 --- a/management/internals/modules/reverseproxy/proxy/manager.go +++ b/management/internals/modules/reverseproxy/proxy/manager.go @@ -1,6 +1,6 @@ package proxy -//go:generate go run github.com/golang/mock/mockgen -package proxy -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod +//go:generate go tool mockgen -package proxy -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod import ( "context" diff --git a/management/internals/modules/reverseproxy/proxy/manager_mock.go b/management/internals/modules/reverseproxy/proxy/manager_mock.go index d2be46c9f..36d6f53fc 100644 --- a/management/internals/modules/reverseproxy/proxy/manager_mock.go +++ b/management/internals/modules/reverseproxy/proxy/manager_mock.go @@ -1,5 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: ./manager.go +// +// Generated by this command: +// +// mockgen -package proxy -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod +// // Package proxy is a generated GoMock package. package proxy @@ -9,14 +14,15 @@ import ( reflect "reflect" time "time" - gomock "github.com/golang/mock/gomock" proto "github.com/netbirdio/netbird/shared/management/proto" + gomock "go.uber.org/mock/gomock" ) // MockManager is a mock of Manager interface. type MockManager struct { ctrl *gomock.Controller recorder *MockManagerMockRecorder + isgomock struct{} } // MockManagerMockRecorder is the mock recorder for MockManager. @@ -45,25 +51,11 @@ func (m *MockManager) CleanupStale(ctx context.Context, inactivityDuration time. } // CleanupStale indicates an expected call of CleanupStale. -func (mr *MockManagerMockRecorder) CleanupStale(ctx, inactivityDuration interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) CleanupStale(ctx, inactivityDuration any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CleanupStale", reflect.TypeOf((*MockManager)(nil).CleanupStale), ctx, inactivityDuration) } -// ClusterSupportsCustomPorts mocks base method. -func (m *MockManager) ClusterSupportsCustomPorts(ctx context.Context, clusterAddr string) *bool { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ClusterSupportsCustomPorts", ctx, clusterAddr) - ret0, _ := ret[0].(*bool) - return ret0 -} - -// ClusterSupportsCustomPorts indicates an expected call of ClusterSupportsCustomPorts. -func (mr *MockManagerMockRecorder) ClusterSupportsCustomPorts(ctx, clusterAddr interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClusterSupportsCustomPorts", reflect.TypeOf((*MockManager)(nil).ClusterSupportsCustomPorts), ctx, clusterAddr) -} - // ClusterRequireSubdomain mocks base method. func (m *MockManager) ClusterRequireSubdomain(ctx context.Context, clusterAddr string) *bool { m.ctrl.T.Helper() @@ -73,7 +65,7 @@ func (m *MockManager) ClusterRequireSubdomain(ctx context.Context, clusterAddr s } // ClusterRequireSubdomain indicates an expected call of ClusterRequireSubdomain. -func (mr *MockManagerMockRecorder) ClusterRequireSubdomain(ctx, clusterAddr interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) ClusterRequireSubdomain(ctx, clusterAddr any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClusterRequireSubdomain", reflect.TypeOf((*MockManager)(nil).ClusterRequireSubdomain), ctx, clusterAddr) } @@ -87,11 +79,25 @@ func (m *MockManager) ClusterSupportsCrowdSec(ctx context.Context, clusterAddr s } // ClusterSupportsCrowdSec indicates an expected call of ClusterSupportsCrowdSec. -func (mr *MockManagerMockRecorder) ClusterSupportsCrowdSec(ctx, clusterAddr interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) ClusterSupportsCrowdSec(ctx, clusterAddr any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClusterSupportsCrowdSec", reflect.TypeOf((*MockManager)(nil).ClusterSupportsCrowdSec), ctx, clusterAddr) } +// ClusterSupportsCustomPorts mocks base method. +func (m *MockManager) ClusterSupportsCustomPorts(ctx context.Context, clusterAddr string) *bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ClusterSupportsCustomPorts", ctx, clusterAddr) + ret0, _ := ret[0].(*bool) + return ret0 +} + +// ClusterSupportsCustomPorts indicates an expected call of ClusterSupportsCustomPorts. +func (mr *MockManagerMockRecorder) ClusterSupportsCustomPorts(ctx, clusterAddr any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClusterSupportsCustomPorts", reflect.TypeOf((*MockManager)(nil).ClusterSupportsCustomPorts), ctx, clusterAddr) +} + // ClusterSupportsPrivate mocks base method. func (m *MockManager) ClusterSupportsPrivate(ctx context.Context, clusterAddr string) *bool { m.ctrl.T.Helper() @@ -101,7 +107,7 @@ func (m *MockManager) ClusterSupportsPrivate(ctx context.Context, clusterAddr st } // ClusterSupportsPrivate indicates an expected call of ClusterSupportsPrivate. -func (mr *MockManagerMockRecorder) ClusterSupportsPrivate(ctx, clusterAddr interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) ClusterSupportsPrivate(ctx, clusterAddr any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClusterSupportsPrivate", reflect.TypeOf((*MockManager)(nil).ClusterSupportsPrivate), ctx, clusterAddr) } @@ -116,11 +122,40 @@ func (m *MockManager) Connect(ctx context.Context, proxyID, sessionID, clusterAd } // Connect indicates an expected call of Connect. -func (mr *MockManagerMockRecorder) Connect(ctx, proxyID, sessionID, clusterAddress, ipAddress, accountID, capabilities interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) Connect(ctx, proxyID, sessionID, clusterAddress, ipAddress, accountID, capabilities any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Connect", reflect.TypeOf((*MockManager)(nil).Connect), ctx, proxyID, sessionID, clusterAddress, ipAddress, accountID, capabilities) } +// CountAccountProxies mocks base method. +func (m *MockManager) CountAccountProxies(ctx context.Context, accountID string) (int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CountAccountProxies", ctx, accountID) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CountAccountProxies indicates an expected call of CountAccountProxies. +func (mr *MockManagerMockRecorder) CountAccountProxies(ctx, accountID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountAccountProxies", reflect.TypeOf((*MockManager)(nil).CountAccountProxies), ctx, accountID) +} + +// DeleteAccountCluster mocks base method. +func (m *MockManager) DeleteAccountCluster(ctx context.Context, clusterAddress, accountID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteAccountCluster", ctx, clusterAddress, accountID) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteAccountCluster indicates an expected call of DeleteAccountCluster. +func (mr *MockManagerMockRecorder) DeleteAccountCluster(ctx, clusterAddress, accountID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAccountCluster", reflect.TypeOf((*MockManager)(nil).DeleteAccountCluster), ctx, clusterAddress, accountID) +} + // Disconnect mocks base method. func (m *MockManager) Disconnect(ctx context.Context, proxyID, sessionID string) error { m.ctrl.T.Helper() @@ -130,11 +165,26 @@ func (m *MockManager) Disconnect(ctx context.Context, proxyID, sessionID string) } // Disconnect indicates an expected call of Disconnect. -func (mr *MockManagerMockRecorder) Disconnect(ctx, proxyID, sessionID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) Disconnect(ctx, proxyID, sessionID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Disconnect", reflect.TypeOf((*MockManager)(nil).Disconnect), ctx, proxyID, sessionID) } +// GetAccountProxy mocks base method. +func (m *MockManager) GetAccountProxy(ctx context.Context, accountID string) (*Proxy, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAccountProxy", ctx, accountID) + ret0, _ := ret[0].(*Proxy) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAccountProxy indicates an expected call of GetAccountProxy. +func (mr *MockManagerMockRecorder) GetAccountProxy(ctx, accountID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountProxy", reflect.TypeOf((*MockManager)(nil).GetAccountProxy), ctx, accountID) +} + // GetActiveClusterAddresses mocks base method. func (m *MockManager) GetActiveClusterAddresses(ctx context.Context) ([]string, error) { m.ctrl.T.Helper() @@ -145,11 +195,12 @@ func (m *MockManager) GetActiveClusterAddresses(ctx context.Context) ([]string, } // GetActiveClusterAddresses indicates an expected call of GetActiveClusterAddresses. -func (mr *MockManagerMockRecorder) GetActiveClusterAddresses(ctx interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetActiveClusterAddresses(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveClusterAddresses", reflect.TypeOf((*MockManager)(nil).GetActiveClusterAddresses), ctx) } +// GetActiveClusterAddressesForAccount mocks base method. func (m *MockManager) GetActiveClusterAddressesForAccount(ctx context.Context, accountID string) ([]string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetActiveClusterAddressesForAccount", ctx, accountID) @@ -158,7 +209,8 @@ func (m *MockManager) GetActiveClusterAddressesForAccount(ctx context.Context, a return ret0, ret1 } -func (mr *MockManagerMockRecorder) GetActiveClusterAddressesForAccount(ctx, accountID interface{}) *gomock.Call { +// GetActiveClusterAddressesForAccount indicates an expected call of GetActiveClusterAddressesForAccount. +func (mr *MockManagerMockRecorder) GetActiveClusterAddressesForAccount(ctx, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveClusterAddressesForAccount", reflect.TypeOf((*MockManager)(nil).GetActiveClusterAddressesForAccount), ctx, accountID) } @@ -172,41 +224,11 @@ func (m *MockManager) Heartbeat(ctx context.Context, p *Proxy) error { } // Heartbeat indicates an expected call of Heartbeat. -func (mr *MockManagerMockRecorder) Heartbeat(ctx, p interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) Heartbeat(ctx, p any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Heartbeat", reflect.TypeOf((*MockManager)(nil).Heartbeat), ctx, p) } -// GetAccountProxy mocks base method. -func (m *MockManager) GetAccountProxy(ctx context.Context, accountID string) (*Proxy, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAccountProxy", ctx, accountID) - ret0, _ := ret[0].(*Proxy) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAccountProxy indicates an expected call of GetAccountProxy. -func (mr *MockManagerMockRecorder) GetAccountProxy(ctx, accountID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountProxy", reflect.TypeOf((*MockManager)(nil).GetAccountProxy), ctx, accountID) -} - -// CountAccountProxies mocks base method. -func (m *MockManager) CountAccountProxies(ctx context.Context, accountID string) (int64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CountAccountProxies", ctx, accountID) - ret0, _ := ret[0].(int64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CountAccountProxies indicates an expected call of CountAccountProxies. -func (mr *MockManagerMockRecorder) CountAccountProxies(ctx, accountID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountAccountProxies", reflect.TypeOf((*MockManager)(nil).CountAccountProxies), ctx, accountID) -} - // IsClusterAddressAvailable mocks base method. func (m *MockManager) IsClusterAddressAvailable(ctx context.Context, clusterAddress, accountID string) (bool, error) { m.ctrl.T.Helper() @@ -217,29 +239,16 @@ func (m *MockManager) IsClusterAddressAvailable(ctx context.Context, clusterAddr } // IsClusterAddressAvailable indicates an expected call of IsClusterAddressAvailable. -func (mr *MockManagerMockRecorder) IsClusterAddressAvailable(ctx, clusterAddress, accountID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) IsClusterAddressAvailable(ctx, clusterAddress, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsClusterAddressAvailable", reflect.TypeOf((*MockManager)(nil).IsClusterAddressAvailable), ctx, clusterAddress, accountID) } -// DeleteAccountCluster mocks base method. -func (m *MockManager) DeleteAccountCluster(ctx context.Context, clusterAddress, accountID string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteAccountCluster", ctx, clusterAddress, accountID) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteAccountCluster indicates an expected call of DeleteAccountCluster. -func (mr *MockManagerMockRecorder) DeleteAccountCluster(ctx, clusterAddress, accountID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAccountCluster", reflect.TypeOf((*MockManager)(nil).DeleteAccountCluster), ctx, clusterAddress, accountID) -} - // MockController is a mock of Controller interface. type MockController struct { ctrl *gomock.Controller recorder *MockControllerMockRecorder + isgomock struct{} } // MockControllerMockRecorder is the mock recorder for MockController. @@ -282,7 +291,7 @@ func (m *MockController) GetProxiesForCluster(clusterAddr string) []string { } // GetProxiesForCluster indicates an expected call of GetProxiesForCluster. -func (mr *MockControllerMockRecorder) GetProxiesForCluster(clusterAddr interface{}) *gomock.Call { +func (mr *MockControllerMockRecorder) GetProxiesForCluster(clusterAddr any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProxiesForCluster", reflect.TypeOf((*MockController)(nil).GetProxiesForCluster), clusterAddr) } @@ -296,7 +305,7 @@ func (m *MockController) RegisterProxyToCluster(ctx context.Context, clusterAddr } // RegisterProxyToCluster indicates an expected call of RegisterProxyToCluster. -func (mr *MockControllerMockRecorder) RegisterProxyToCluster(ctx, clusterAddr, proxyID interface{}) *gomock.Call { +func (mr *MockControllerMockRecorder) RegisterProxyToCluster(ctx, clusterAddr, proxyID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterProxyToCluster", reflect.TypeOf((*MockController)(nil).RegisterProxyToCluster), ctx, clusterAddr, proxyID) } @@ -308,7 +317,7 @@ func (m *MockController) SendServiceUpdateToCluster(ctx context.Context, account } // SendServiceUpdateToCluster indicates an expected call of SendServiceUpdateToCluster. -func (mr *MockControllerMockRecorder) SendServiceUpdateToCluster(ctx, accountID, update, clusterAddr interface{}) *gomock.Call { +func (mr *MockControllerMockRecorder) SendServiceUpdateToCluster(ctx, accountID, update, clusterAddr any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendServiceUpdateToCluster", reflect.TypeOf((*MockController)(nil).SendServiceUpdateToCluster), ctx, accountID, update, clusterAddr) } @@ -322,7 +331,7 @@ func (m *MockController) UnregisterProxyFromCluster(ctx context.Context, cluster } // UnregisterProxyFromCluster indicates an expected call of UnregisterProxyFromCluster. -func (mr *MockControllerMockRecorder) UnregisterProxyFromCluster(ctx, clusterAddr, proxyID interface{}) *gomock.Call { +func (mr *MockControllerMockRecorder) UnregisterProxyFromCluster(ctx, clusterAddr, proxyID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnregisterProxyFromCluster", reflect.TypeOf((*MockController)(nil).UnregisterProxyFromCluster), ctx, clusterAddr, proxyID) } diff --git a/management/internals/modules/reverseproxy/proxytoken/handler_test.go b/management/internals/modules/reverseproxy/proxytoken/handler_test.go index a5b5713c6..c71fe59f6 100644 --- a/management/internals/modules/reverseproxy/proxytoken/handler_test.go +++ b/management/internals/modules/reverseproxy/proxytoken/handler_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/management/internals/modules/reverseproxy/service/interface.go b/management/internals/modules/reverseproxy/service/interface.go index dddf6ae8a..10d93294a 100644 --- a/management/internals/modules/reverseproxy/service/interface.go +++ b/management/internals/modules/reverseproxy/service/interface.go @@ -1,6 +1,6 @@ package service -//go:generate go run github.com/golang/mock/mockgen -package service -destination=interface_mock.go -source=./interface.go -build_flags=-mod=mod +//go:generate go tool mockgen -package service -destination=interface_mock.go -source=./interface.go -build_flags=-mod=mod import ( "context" diff --git a/management/internals/modules/reverseproxy/service/interface_mock.go b/management/internals/modules/reverseproxy/service/interface_mock.go index 24963fe30..6b60f2af1 100644 --- a/management/internals/modules/reverseproxy/service/interface_mock.go +++ b/management/internals/modules/reverseproxy/service/interface_mock.go @@ -1,5 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: ./interface.go +// +// Generated by this command: +// +// mockgen -package service -destination=interface_mock.go -source=./interface.go -build_flags=-mod=mod +// // Package service is a generated GoMock package. package service @@ -8,14 +13,15 @@ import ( context "context" reflect "reflect" - gomock "github.com/golang/mock/gomock" proxy "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/proxy" + gomock "go.uber.org/mock/gomock" ) // MockManager is a mock of Manager interface. type MockManager struct { ctrl *gomock.Controller recorder *MockManagerMockRecorder + isgomock struct{} } // MockManagerMockRecorder is the mock recorder for MockManager. @@ -45,7 +51,7 @@ func (m *MockManager) CreateService(ctx context.Context, accountID, userID strin } // CreateService indicates an expected call of CreateService. -func (mr *MockManagerMockRecorder) CreateService(ctx, accountID, userID, service interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) CreateService(ctx, accountID, userID, service any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateService", reflect.TypeOf((*MockManager)(nil).CreateService), ctx, accountID, userID, service) } @@ -60,7 +66,7 @@ func (m *MockManager) CreateServiceFromPeer(ctx context.Context, accountID, peer } // CreateServiceFromPeer indicates an expected call of CreateServiceFromPeer. -func (mr *MockManagerMockRecorder) CreateServiceFromPeer(ctx, accountID, peerID, req interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) CreateServiceFromPeer(ctx, accountID, peerID, req any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateServiceFromPeer", reflect.TypeOf((*MockManager)(nil).CreateServiceFromPeer), ctx, accountID, peerID, req) } @@ -74,7 +80,7 @@ func (m *MockManager) DeleteAccountCluster(ctx context.Context, accountID, userI } // DeleteAccountCluster indicates an expected call of DeleteAccountCluster. -func (mr *MockManagerMockRecorder) DeleteAccountCluster(ctx, accountID, userID, clusterAddress interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) DeleteAccountCluster(ctx, accountID, userID, clusterAddress any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAccountCluster", reflect.TypeOf((*MockManager)(nil).DeleteAccountCluster), ctx, accountID, userID, clusterAddress) } @@ -88,7 +94,7 @@ func (m *MockManager) DeleteAllServices(ctx context.Context, accountID, userID s } // DeleteAllServices indicates an expected call of DeleteAllServices. -func (mr *MockManagerMockRecorder) DeleteAllServices(ctx, accountID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) DeleteAllServices(ctx, accountID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAllServices", reflect.TypeOf((*MockManager)(nil).DeleteAllServices), ctx, accountID, userID) } @@ -102,7 +108,7 @@ func (m *MockManager) DeleteService(ctx context.Context, accountID, userID, serv } // DeleteService indicates an expected call of DeleteService. -func (mr *MockManagerMockRecorder) DeleteService(ctx, accountID, userID, serviceID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) DeleteService(ctx, accountID, userID, serviceID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteService", reflect.TypeOf((*MockManager)(nil).DeleteService), ctx, accountID, userID, serviceID) } @@ -117,7 +123,7 @@ func (m *MockManager) GetAccountServices(ctx context.Context, accountID string) } // GetAccountServices indicates an expected call of GetAccountServices. -func (mr *MockManagerMockRecorder) GetAccountServices(ctx, accountID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetAccountServices(ctx, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountServices", reflect.TypeOf((*MockManager)(nil).GetAccountServices), ctx, accountID) } @@ -132,7 +138,7 @@ func (m *MockManager) GetAllServices(ctx context.Context, accountID, userID stri } // GetAllServices indicates an expected call of GetAllServices. -func (mr *MockManagerMockRecorder) GetAllServices(ctx, accountID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetAllServices(ctx, accountID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllServices", reflect.TypeOf((*MockManager)(nil).GetAllServices), ctx, accountID, userID) } @@ -147,7 +153,7 @@ func (m *MockManager) GetClusters(ctx context.Context, accountID, userID string) } // GetClusters indicates an expected call of GetClusters. -func (mr *MockManagerMockRecorder) GetClusters(ctx, accountID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetClusters(ctx, accountID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClusters", reflect.TypeOf((*MockManager)(nil).GetClusters), ctx, accountID, userID) } @@ -162,7 +168,7 @@ func (m *MockManager) GetGlobalServices(ctx context.Context) ([]*Service, error) } // GetGlobalServices indicates an expected call of GetGlobalServices. -func (mr *MockManagerMockRecorder) GetGlobalServices(ctx interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetGlobalServices(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGlobalServices", reflect.TypeOf((*MockManager)(nil).GetGlobalServices), ctx) } @@ -177,7 +183,7 @@ func (m *MockManager) GetService(ctx context.Context, accountID, userID, service } // GetService indicates an expected call of GetService. -func (mr *MockManagerMockRecorder) GetService(ctx, accountID, userID, serviceID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetService(ctx, accountID, userID, serviceID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetService", reflect.TypeOf((*MockManager)(nil).GetService), ctx, accountID, userID, serviceID) } @@ -192,7 +198,7 @@ func (m *MockManager) GetServiceByDomain(ctx context.Context, domain string) (*S } // GetServiceByDomain indicates an expected call of GetServiceByDomain. -func (mr *MockManagerMockRecorder) GetServiceByDomain(ctx, domain interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetServiceByDomain(ctx, domain any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceByDomain", reflect.TypeOf((*MockManager)(nil).GetServiceByDomain), ctx, domain) } @@ -207,7 +213,7 @@ func (m *MockManager) GetServiceByID(ctx context.Context, accountID, serviceID s } // GetServiceByID indicates an expected call of GetServiceByID. -func (mr *MockManagerMockRecorder) GetServiceByID(ctx, accountID, serviceID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetServiceByID(ctx, accountID, serviceID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceByID", reflect.TypeOf((*MockManager)(nil).GetServiceByID), ctx, accountID, serviceID) } @@ -222,7 +228,7 @@ func (m *MockManager) GetServiceIDByTargetID(ctx context.Context, accountID, res } // GetServiceIDByTargetID indicates an expected call of GetServiceIDByTargetID. -func (mr *MockManagerMockRecorder) GetServiceIDByTargetID(ctx, accountID, resourceID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetServiceIDByTargetID(ctx, accountID, resourceID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceIDByTargetID", reflect.TypeOf((*MockManager)(nil).GetServiceIDByTargetID), ctx, accountID, resourceID) } @@ -236,7 +242,7 @@ func (m *MockManager) ReloadAllServicesForAccount(ctx context.Context, accountID } // ReloadAllServicesForAccount indicates an expected call of ReloadAllServicesForAccount. -func (mr *MockManagerMockRecorder) ReloadAllServicesForAccount(ctx, accountID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) ReloadAllServicesForAccount(ctx, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReloadAllServicesForAccount", reflect.TypeOf((*MockManager)(nil).ReloadAllServicesForAccount), ctx, accountID) } @@ -250,7 +256,7 @@ func (m *MockManager) ReloadService(ctx context.Context, accountID, serviceID st } // ReloadService indicates an expected call of ReloadService. -func (mr *MockManagerMockRecorder) ReloadService(ctx, accountID, serviceID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) ReloadService(ctx, accountID, serviceID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReloadService", reflect.TypeOf((*MockManager)(nil).ReloadService), ctx, accountID, serviceID) } @@ -264,7 +270,7 @@ func (m *MockManager) RenewServiceFromPeer(ctx context.Context, accountID, peerI } // RenewServiceFromPeer indicates an expected call of RenewServiceFromPeer. -func (mr *MockManagerMockRecorder) RenewServiceFromPeer(ctx, accountID, peerID, serviceID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) RenewServiceFromPeer(ctx, accountID, peerID, serviceID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RenewServiceFromPeer", reflect.TypeOf((*MockManager)(nil).RenewServiceFromPeer), ctx, accountID, peerID, serviceID) } @@ -278,7 +284,7 @@ func (m *MockManager) SetCertificateIssuedAt(ctx context.Context, accountID, ser } // SetCertificateIssuedAt indicates an expected call of SetCertificateIssuedAt. -func (mr *MockManagerMockRecorder) SetCertificateIssuedAt(ctx, accountID, serviceID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) SetCertificateIssuedAt(ctx, accountID, serviceID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetCertificateIssuedAt", reflect.TypeOf((*MockManager)(nil).SetCertificateIssuedAt), ctx, accountID, serviceID) } @@ -292,7 +298,7 @@ func (m *MockManager) SetStatus(ctx context.Context, accountID, serviceID string } // SetStatus indicates an expected call of SetStatus. -func (mr *MockManagerMockRecorder) SetStatus(ctx, accountID, serviceID, status interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) SetStatus(ctx, accountID, serviceID, status any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetStatus", reflect.TypeOf((*MockManager)(nil).SetStatus), ctx, accountID, serviceID, status) } @@ -304,7 +310,7 @@ func (m *MockManager) StartExposeReaper(ctx context.Context) { } // StartExposeReaper indicates an expected call of StartExposeReaper. -func (mr *MockManagerMockRecorder) StartExposeReaper(ctx interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) StartExposeReaper(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartExposeReaper", reflect.TypeOf((*MockManager)(nil).StartExposeReaper), ctx) } @@ -318,7 +324,7 @@ func (m *MockManager) StopServiceFromPeer(ctx context.Context, accountID, peerID } // StopServiceFromPeer indicates an expected call of StopServiceFromPeer. -func (mr *MockManagerMockRecorder) StopServiceFromPeer(ctx, accountID, peerID, serviceID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) StopServiceFromPeer(ctx, accountID, peerID, serviceID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StopServiceFromPeer", reflect.TypeOf((*MockManager)(nil).StopServiceFromPeer), ctx, accountID, peerID, serviceID) } @@ -333,7 +339,7 @@ func (m *MockManager) UpdateService(ctx context.Context, accountID, userID strin } // UpdateService indicates an expected call of UpdateService. -func (mr *MockManagerMockRecorder) UpdateService(ctx, accountID, userID, service interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) UpdateService(ctx, accountID, userID, service any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateService", reflect.TypeOf((*MockManager)(nil).UpdateService), ctx, accountID, userID, service) } diff --git a/management/internals/modules/reverseproxy/service/manager/l4_port_test.go b/management/internals/modules/reverseproxy/service/manager/l4_port_test.go index c218291ef..a44e759c4 100644 --- a/management/internals/modules/reverseproxy/service/manager/l4_port_test.go +++ b/management/internals/modules/reverseproxy/service/manager/l4_port_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/management/internals/modules/reverseproxy/service/manager/manager_test.go b/management/internals/modules/reverseproxy/service/manager/manager_test.go index 29a117921..10893673e 100644 --- a/management/internals/modules/reverseproxy/service/manager/manager_test.go +++ b/management/internals/modules/reverseproxy/service/manager/manager_test.go @@ -8,7 +8,7 @@ import ( "time" cachestore "github.com/eko/gocache/lib/v4/store" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/metric/noop" diff --git a/management/internals/modules/zones/manager/manager_test.go b/management/internals/modules/zones/manager/manager_test.go index 29e7e8677..f6f1743ce 100644 --- a/management/internals/modules/zones/manager/manager_test.go +++ b/management/internals/modules/zones/manager/manager_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/management/internals/modules/zones/records/manager/manager_test.go b/management/internals/modules/zones/records/manager/manager_test.go index a5f48c4a9..e5ed26509 100644 --- a/management/internals/modules/zones/records/manager/manager_test.go +++ b/management/internals/modules/zones/records/manager/manager_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/management/internals/server/server_resolve_domains_test.go b/management/internals/server/server_resolve_domains_test.go index ba9eb3f74..b34369655 100644 --- a/management/internals/server/server_resolve_domains_test.go +++ b/management/internals/server/server_resolve_domains_test.go @@ -5,7 +5,7 @@ import ( "errors" "testing" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/require" nbconfig "github.com/netbirdio/netbird/management/internals/server/config" diff --git a/management/internals/shared/grpc/conversion.go b/management/internals/shared/grpc/conversion.go index 74ceb3370..2b923836c 100644 --- a/management/internals/shared/grpc/conversion.go +++ b/management/internals/shared/grpc/conversion.go @@ -311,7 +311,7 @@ func buildJWTConfig(config *nbconfig.HttpServerConfig, deviceFlowConfig *nbconfi return &proto.JWTConfig{ Issuer: issuer, - Audience: audience, + Audience: audience, //nolint:staticcheck Audiences: audiences, KeysLocation: keysLocation, } diff --git a/management/internals/shared/grpc/proxy_connect_authorizer_test.go b/management/internals/shared/grpc/proxy_connect_authorizer_test.go index ff618227e..d0d196d20 100644 --- a/management/internals/shared/grpc/proxy_connect_authorizer_test.go +++ b/management/internals/shared/grpc/proxy_connect_authorizer_test.go @@ -5,7 +5,7 @@ import ( "errors" "testing" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" diff --git a/management/internals/shared/grpc/proxy_snapshot_test.go b/management/internals/shared/grpc/proxy_snapshot_test.go index 68d2ecfd1..8b84a849e 100644 --- a/management/internals/shared/grpc/proxy_snapshot_test.go +++ b/management/internals/shared/grpc/proxy_snapshot_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc" diff --git a/management/internals/shared/grpc/server.go b/management/internals/shared/grpc/server.go index 485f05a92..3d5f0a1b7 100644 --- a/management/internals/shared/grpc/server.go +++ b/management/internals/shared/grpc/server.go @@ -1140,7 +1140,7 @@ func (s *Server) GetDeviceAuthorizationFlow(ctx context.Context, req *proto.Encr Provider: proto.DeviceAuthorizationFlowProvider(provider), ProviderConfig: &proto.ProviderConfig{ ClientID: s.config.DeviceAuthorizationFlow.ProviderConfig.ClientID, - ClientSecret: s.config.DeviceAuthorizationFlow.ProviderConfig.ClientSecret, + ClientSecret: s.config.DeviceAuthorizationFlow.ProviderConfig.ClientSecret, //nolint:staticcheck Domain: s.config.DeviceAuthorizationFlow.ProviderConfig.Domain, Audience: s.config.DeviceAuthorizationFlow.ProviderConfig.Audience, DeviceAuthEndpoint: s.config.DeviceAuthorizationFlow.ProviderConfig.DeviceAuthEndpoint, @@ -1211,7 +1211,7 @@ func (s *Server) GetPKCEAuthorizationFlow(ctx context.Context, req *proto.Encryp ProviderConfig: &proto.ProviderConfig{ Audience: s.config.PKCEAuthorizationFlow.ProviderConfig.Audience, ClientID: s.config.PKCEAuthorizationFlow.ProviderConfig.ClientID, - ClientSecret: s.config.PKCEAuthorizationFlow.ProviderConfig.ClientSecret, + ClientSecret: s.config.PKCEAuthorizationFlow.ProviderConfig.ClientSecret, //nolint:staticcheck TokenEndpoint: s.config.PKCEAuthorizationFlow.ProviderConfig.TokenEndpoint, AuthorizationEndpoint: s.config.PKCEAuthorizationFlow.ProviderConfig.AuthorizationEndpoint, Scope: s.config.PKCEAuthorizationFlow.ProviderConfig.Scope, diff --git a/management/internals/shared/grpc/sync_mappings_test.go b/management/internals/shared/grpc/sync_mappings_test.go index 97f6183bb..6db43d7c2 100644 --- a/management/internals/shared/grpc/sync_mappings_test.go +++ b/management/internals/shared/grpc/sync_mappings_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc" diff --git a/management/internals/shared/grpc/token_mgr_test.go b/management/internals/shared/grpc/token_mgr_test.go index 98eb66fb5..b1be5f99a 100644 --- a/management/internals/shared/grpc/token_mgr_test.go +++ b/management/internals/shared/grpc/token_mgr_test.go @@ -10,7 +10,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/require" "github.com/netbirdio/netbird/management/internals/controllers/network_map" diff --git a/management/server/account/manager.go b/management/server/account/manager.go index 1e738c274..f4b0408cf 100644 --- a/management/server/account/manager.go +++ b/management/server/account/manager.go @@ -1,6 +1,6 @@ package account -//go:generate go run github.com/golang/mock/mockgen -package account -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod +//go:generate go tool mockgen -package account -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod import ( "context" diff --git a/management/server/account/manager_mock.go b/management/server/account/manager_mock.go index 274e4c683..9ac10cba0 100644 --- a/management/server/account/manager_mock.go +++ b/management/server/account/manager_mock.go @@ -1,5 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: ./manager.go +// +// Generated by this command: +// +// mockgen -package account -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod +// // Package account is a generated GoMock package. package account @@ -11,7 +16,6 @@ import ( reflect "reflect" time "time" - gomock "github.com/golang/mock/gomock" dns "github.com/netbirdio/netbird/dns" service "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/service" activity "github.com/netbirdio/netbird/management/server/activity" @@ -25,12 +29,14 @@ import ( route "github.com/netbirdio/netbird/route" auth "github.com/netbirdio/netbird/shared/auth" domain "github.com/netbirdio/netbird/shared/management/domain" + gomock "go.uber.org/mock/gomock" ) // MockManager is a mock of Manager interface. type MockManager struct { ctrl *gomock.Controller recorder *MockManagerMockRecorder + isgomock struct{} } // MockManagerMockRecorder is the mock recorder for MockManager. @@ -59,7 +65,7 @@ func (m *MockManager) AcceptUserInvite(ctx context.Context, token, password stri } // AcceptUserInvite indicates an expected call of AcceptUserInvite. -func (mr *MockManagerMockRecorder) AcceptUserInvite(ctx, token, password interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) AcceptUserInvite(ctx, token, password any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcceptUserInvite", reflect.TypeOf((*MockManager)(nil).AcceptUserInvite), ctx, token, password) } @@ -74,7 +80,7 @@ func (m *MockManager) AccountExists(ctx context.Context, accountID string) (bool } // AccountExists indicates an expected call of AccountExists. -func (mr *MockManagerMockRecorder) AccountExists(ctx, accountID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) AccountExists(ctx, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AccountExists", reflect.TypeOf((*MockManager)(nil).AccountExists), ctx, accountID) } @@ -92,7 +98,7 @@ func (m *MockManager) AddPeer(ctx context.Context, accountID, setupKey, userID s } // AddPeer indicates an expected call of AddPeer. -func (mr *MockManagerMockRecorder) AddPeer(ctx, accountID, setupKey, userID, p, temporary interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) AddPeer(ctx, accountID, setupKey, userID, p, temporary any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddPeer", reflect.TypeOf((*MockManager)(nil).AddPeer), ctx, accountID, setupKey, userID, p, temporary) } @@ -107,7 +113,7 @@ func (m *MockManager) ApproveUser(ctx context.Context, accountID, initiatorUserI } // ApproveUser indicates an expected call of ApproveUser. -func (mr *MockManagerMockRecorder) ApproveUser(ctx, accountID, initiatorUserID, targetUserID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) ApproveUser(ctx, accountID, initiatorUserID, targetUserID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApproveUser", reflect.TypeOf((*MockManager)(nil).ApproveUser), ctx, accountID, initiatorUserID, targetUserID) } @@ -119,7 +125,7 @@ func (m *MockManager) BufferUpdateAccountPeers(ctx context.Context, accountID st } // BufferUpdateAccountPeers indicates an expected call of BufferUpdateAccountPeers. -func (mr *MockManagerMockRecorder) BufferUpdateAccountPeers(ctx, accountID, reason interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) BufferUpdateAccountPeers(ctx, accountID, reason any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BufferUpdateAccountPeers", reflect.TypeOf((*MockManager)(nil).BufferUpdateAccountPeers), ctx, accountID, reason) } @@ -134,7 +140,7 @@ func (m *MockManager) BuildUserInfosForAccount(ctx context.Context, accountID, i } // BuildUserInfosForAccount indicates an expected call of BuildUserInfosForAccount. -func (mr *MockManagerMockRecorder) BuildUserInfosForAccount(ctx, accountID, initiatorUserID, accountUsers interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) BuildUserInfosForAccount(ctx, accountID, initiatorUserID, accountUsers any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BuildUserInfosForAccount", reflect.TypeOf((*MockManager)(nil).BuildUserInfosForAccount), ctx, accountID, initiatorUserID, accountUsers) } @@ -148,7 +154,7 @@ func (m *MockManager) CreateGroup(ctx context.Context, accountID, userID string, } // CreateGroup indicates an expected call of CreateGroup. -func (mr *MockManagerMockRecorder) CreateGroup(ctx, accountID, userID, group interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) CreateGroup(ctx, accountID, userID, group any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateGroup", reflect.TypeOf((*MockManager)(nil).CreateGroup), ctx, accountID, userID, group) } @@ -162,24 +168,24 @@ func (m *MockManager) CreateGroups(ctx context.Context, accountID, userID string } // CreateGroups indicates an expected call of CreateGroups. -func (mr *MockManagerMockRecorder) CreateGroups(ctx, accountID, userID, newGroups interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) CreateGroups(ctx, accountID, userID, newGroups any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateGroups", reflect.TypeOf((*MockManager)(nil).CreateGroups), ctx, accountID, userID, newGroups) } // CreateIdentityProvider mocks base method. -func (m *MockManager) CreateIdentityProvider(ctx context.Context, accountID, userID string, idp *types.IdentityProvider) (*types.IdentityProvider, error) { +func (m *MockManager) CreateIdentityProvider(ctx context.Context, accountID, userID string, arg3 *types.IdentityProvider) (*types.IdentityProvider, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateIdentityProvider", ctx, accountID, userID, idp) + ret := m.ctrl.Call(m, "CreateIdentityProvider", ctx, accountID, userID, arg3) ret0, _ := ret[0].(*types.IdentityProvider) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateIdentityProvider indicates an expected call of CreateIdentityProvider. -func (mr *MockManagerMockRecorder) CreateIdentityProvider(ctx, accountID, userID, idp interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) CreateIdentityProvider(ctx, accountID, userID, arg3 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateIdentityProvider", reflect.TypeOf((*MockManager)(nil).CreateIdentityProvider), ctx, accountID, userID, idp) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateIdentityProvider", reflect.TypeOf((*MockManager)(nil).CreateIdentityProvider), ctx, accountID, userID, arg3) } // CreateNameServerGroup mocks base method. @@ -192,7 +198,7 @@ func (m *MockManager) CreateNameServerGroup(ctx context.Context, accountID, name } // CreateNameServerGroup indicates an expected call of CreateNameServerGroup. -func (mr *MockManagerMockRecorder) CreateNameServerGroup(ctx, accountID, name, description, nameServerList, groups, primary, domains, enabled, userID, searchDomainsEnabled interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) CreateNameServerGroup(ctx, accountID, name, description, nameServerList, groups, primary, domains, enabled, userID, searchDomainsEnabled any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNameServerGroup", reflect.TypeOf((*MockManager)(nil).CreateNameServerGroup), ctx, accountID, name, description, nameServerList, groups, primary, domains, enabled, userID, searchDomainsEnabled) } @@ -207,7 +213,7 @@ func (m *MockManager) CreatePAT(ctx context.Context, accountID, initiatorUserID, } // CreatePAT indicates an expected call of CreatePAT. -func (mr *MockManagerMockRecorder) CreatePAT(ctx, accountID, initiatorUserID, targetUserID, tokenName, expiresIn interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) CreatePAT(ctx, accountID, initiatorUserID, targetUserID, tokenName, expiresIn any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatePAT", reflect.TypeOf((*MockManager)(nil).CreatePAT), ctx, accountID, initiatorUserID, targetUserID, tokenName, expiresIn) } @@ -221,7 +227,7 @@ func (m *MockManager) CreatePeerJob(ctx context.Context, accountID, peerID, user } // CreatePeerJob indicates an expected call of CreatePeerJob. -func (mr *MockManagerMockRecorder) CreatePeerJob(ctx, accountID, peerID, userID, job interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) CreatePeerJob(ctx, accountID, peerID, userID, job any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatePeerJob", reflect.TypeOf((*MockManager)(nil).CreatePeerJob), ctx, accountID, peerID, userID, job) } @@ -236,7 +242,7 @@ func (m *MockManager) CreateRoute(ctx context.Context, accountID string, prefix } // CreateRoute indicates an expected call of CreateRoute. -func (mr *MockManagerMockRecorder) CreateRoute(ctx, accountID, prefix, networkType, domains, peerID, peerGroupIDs, description, netID, masquerade, metric, groups, accessControlGroupIDs, enabled, userID, keepRoute, skipAutoApply interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) CreateRoute(ctx, accountID, prefix, networkType, domains, peerID, peerGroupIDs, description, netID, masquerade, metric, groups, accessControlGroupIDs, enabled, userID, keepRoute, skipAutoApply any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateRoute", reflect.TypeOf((*MockManager)(nil).CreateRoute), ctx, accountID, prefix, networkType, domains, peerID, peerGroupIDs, description, netID, masquerade, metric, groups, accessControlGroupIDs, enabled, userID, keepRoute, skipAutoApply) } @@ -251,7 +257,7 @@ func (m *MockManager) CreateSetupKey(ctx context.Context, accountID, keyName str } // CreateSetupKey indicates an expected call of CreateSetupKey. -func (mr *MockManagerMockRecorder) CreateSetupKey(ctx, accountID, keyName, keyType, expiresIn, autoGroups, usageLimit, userID, ephemeral, allowExtraDNSLabels interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) CreateSetupKey(ctx, accountID, keyName, keyType, expiresIn, autoGroups, usageLimit, userID, ephemeral, allowExtraDNSLabels any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSetupKey", reflect.TypeOf((*MockManager)(nil).CreateSetupKey), ctx, accountID, keyName, keyType, expiresIn, autoGroups, usageLimit, userID, ephemeral, allowExtraDNSLabels) } @@ -266,7 +272,7 @@ func (m *MockManager) CreateUser(ctx context.Context, accountID, initiatorUserID } // CreateUser indicates an expected call of CreateUser. -func (mr *MockManagerMockRecorder) CreateUser(ctx, accountID, initiatorUserID, key interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) CreateUser(ctx, accountID, initiatorUserID, key any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateUser", reflect.TypeOf((*MockManager)(nil).CreateUser), ctx, accountID, initiatorUserID, key) } @@ -281,7 +287,7 @@ func (m *MockManager) CreateUserInvite(ctx context.Context, accountID, initiator } // CreateUserInvite indicates an expected call of CreateUserInvite. -func (mr *MockManagerMockRecorder) CreateUserInvite(ctx, accountID, initiatorUserID, invite, expiresIn interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) CreateUserInvite(ctx, accountID, initiatorUserID, invite, expiresIn any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateUserInvite", reflect.TypeOf((*MockManager)(nil).CreateUserInvite), ctx, accountID, initiatorUserID, invite, expiresIn) } @@ -295,7 +301,7 @@ func (m *MockManager) DeleteAccount(ctx context.Context, accountID, userID strin } // DeleteAccount indicates an expected call of DeleteAccount. -func (mr *MockManagerMockRecorder) DeleteAccount(ctx, accountID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) DeleteAccount(ctx, accountID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAccount", reflect.TypeOf((*MockManager)(nil).DeleteAccount), ctx, accountID, userID) } @@ -309,7 +315,7 @@ func (m *MockManager) DeleteGroup(ctx context.Context, accountId, userId, groupI } // DeleteGroup indicates an expected call of DeleteGroup. -func (mr *MockManagerMockRecorder) DeleteGroup(ctx, accountId, userId, groupID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) DeleteGroup(ctx, accountId, userId, groupID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteGroup", reflect.TypeOf((*MockManager)(nil).DeleteGroup), ctx, accountId, userId, groupID) } @@ -323,7 +329,7 @@ func (m *MockManager) DeleteGroups(ctx context.Context, accountId, userId string } // DeleteGroups indicates an expected call of DeleteGroups. -func (mr *MockManagerMockRecorder) DeleteGroups(ctx, accountId, userId, groupIDs interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) DeleteGroups(ctx, accountId, userId, groupIDs any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteGroups", reflect.TypeOf((*MockManager)(nil).DeleteGroups), ctx, accountId, userId, groupIDs) } @@ -337,7 +343,7 @@ func (m *MockManager) DeleteIdentityProvider(ctx context.Context, accountID, idp } // DeleteIdentityProvider indicates an expected call of DeleteIdentityProvider. -func (mr *MockManagerMockRecorder) DeleteIdentityProvider(ctx, accountID, idpID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) DeleteIdentityProvider(ctx, accountID, idpID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteIdentityProvider", reflect.TypeOf((*MockManager)(nil).DeleteIdentityProvider), ctx, accountID, idpID, userID) } @@ -351,7 +357,7 @@ func (m *MockManager) DeleteNameServerGroup(ctx context.Context, accountID, nsGr } // DeleteNameServerGroup indicates an expected call of DeleteNameServerGroup. -func (mr *MockManagerMockRecorder) DeleteNameServerGroup(ctx, accountID, nsGroupID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) DeleteNameServerGroup(ctx, accountID, nsGroupID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteNameServerGroup", reflect.TypeOf((*MockManager)(nil).DeleteNameServerGroup), ctx, accountID, nsGroupID, userID) } @@ -365,7 +371,7 @@ func (m *MockManager) DeletePAT(ctx context.Context, accountID, initiatorUserID, } // DeletePAT indicates an expected call of DeletePAT. -func (mr *MockManagerMockRecorder) DeletePAT(ctx, accountID, initiatorUserID, targetUserID, tokenID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) DeletePAT(ctx, accountID, initiatorUserID, targetUserID, tokenID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePAT", reflect.TypeOf((*MockManager)(nil).DeletePAT), ctx, accountID, initiatorUserID, targetUserID, tokenID) } @@ -379,7 +385,7 @@ func (m *MockManager) DeletePeer(ctx context.Context, accountID, peerID, userID } // DeletePeer indicates an expected call of DeletePeer. -func (mr *MockManagerMockRecorder) DeletePeer(ctx, accountID, peerID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) DeletePeer(ctx, accountID, peerID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePeer", reflect.TypeOf((*MockManager)(nil).DeletePeer), ctx, accountID, peerID, userID) } @@ -393,7 +399,7 @@ func (m *MockManager) DeletePolicy(ctx context.Context, accountID, policyID, use } // DeletePolicy indicates an expected call of DeletePolicy. -func (mr *MockManagerMockRecorder) DeletePolicy(ctx, accountID, policyID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) DeletePolicy(ctx, accountID, policyID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePolicy", reflect.TypeOf((*MockManager)(nil).DeletePolicy), ctx, accountID, policyID, userID) } @@ -407,7 +413,7 @@ func (m *MockManager) DeletePostureChecks(ctx context.Context, accountID, postur } // DeletePostureChecks indicates an expected call of DeletePostureChecks. -func (mr *MockManagerMockRecorder) DeletePostureChecks(ctx, accountID, postureChecksID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) DeletePostureChecks(ctx, accountID, postureChecksID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePostureChecks", reflect.TypeOf((*MockManager)(nil).DeletePostureChecks), ctx, accountID, postureChecksID, userID) } @@ -421,7 +427,7 @@ func (m *MockManager) DeleteRegularUsers(ctx context.Context, accountID, initiat } // DeleteRegularUsers indicates an expected call of DeleteRegularUsers. -func (mr *MockManagerMockRecorder) DeleteRegularUsers(ctx, accountID, initiatorUserID, targetUserIDs, userInfos interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) DeleteRegularUsers(ctx, accountID, initiatorUserID, targetUserIDs, userInfos any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteRegularUsers", reflect.TypeOf((*MockManager)(nil).DeleteRegularUsers), ctx, accountID, initiatorUserID, targetUserIDs, userInfos) } @@ -435,7 +441,7 @@ func (m *MockManager) DeleteRoute(ctx context.Context, accountID string, routeID } // DeleteRoute indicates an expected call of DeleteRoute. -func (mr *MockManagerMockRecorder) DeleteRoute(ctx, accountID, routeID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) DeleteRoute(ctx, accountID, routeID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteRoute", reflect.TypeOf((*MockManager)(nil).DeleteRoute), ctx, accountID, routeID, userID) } @@ -449,7 +455,7 @@ func (m *MockManager) DeleteSetupKey(ctx context.Context, accountID, userID, key } // DeleteSetupKey indicates an expected call of DeleteSetupKey. -func (mr *MockManagerMockRecorder) DeleteSetupKey(ctx, accountID, userID, keyID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) DeleteSetupKey(ctx, accountID, userID, keyID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteSetupKey", reflect.TypeOf((*MockManager)(nil).DeleteSetupKey), ctx, accountID, userID, keyID) } @@ -463,7 +469,7 @@ func (m *MockManager) DeleteUser(ctx context.Context, accountID, initiatorUserID } // DeleteUser indicates an expected call of DeleteUser. -func (mr *MockManagerMockRecorder) DeleteUser(ctx, accountID, initiatorUserID, targetUserID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) DeleteUser(ctx, accountID, initiatorUserID, targetUserID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteUser", reflect.TypeOf((*MockManager)(nil).DeleteUser), ctx, accountID, initiatorUserID, targetUserID) } @@ -477,11 +483,38 @@ func (m *MockManager) DeleteUserInvite(ctx context.Context, accountID, initiator } // DeleteUserInvite indicates an expected call of DeleteUserInvite. -func (mr *MockManagerMockRecorder) DeleteUserInvite(ctx, accountID, initiatorUserID, inviteID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) DeleteUserInvite(ctx, accountID, initiatorUserID, inviteID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteUserInvite", reflect.TypeOf((*MockManager)(nil).DeleteUserInvite), ctx, accountID, initiatorUserID, inviteID) } +// ExpandAndUpdateAffected mocks base method. +func (m *MockManager) ExpandAndUpdateAffected(ctx context.Context, accountID string, snap *affectedpeers.Snapshot, change affectedpeers.Change) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ExpandAndUpdateAffected", ctx, accountID, snap, change) +} + +// ExpandAndUpdateAffected indicates an expected call of ExpandAndUpdateAffected. +func (mr *MockManagerMockRecorder) ExpandAndUpdateAffected(ctx, accountID, snap, change any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExpandAndUpdateAffected", reflect.TypeOf((*MockManager)(nil).ExpandAndUpdateAffected), ctx, accountID, snap, change) +} + +// ExtendPeerSession mocks base method. +func (m *MockManager) ExtendPeerSession(ctx context.Context, peerPubKey, userID string) (time.Time, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExtendPeerSession", ctx, peerPubKey, userID) + ret0, _ := ret[0].(time.Time) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ExtendPeerSession indicates an expected call of ExtendPeerSession. +func (mr *MockManagerMockRecorder) ExtendPeerSession(ctx, peerPubKey, userID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExtendPeerSession", reflect.TypeOf((*MockManager)(nil).ExtendPeerSession), ctx, peerPubKey, userID) +} + // FindExistingPostureCheck mocks base method. func (m *MockManager) FindExistingPostureCheck(accountID string, checks *posture.ChecksDefinition) (*posture.Checks, error) { m.ctrl.T.Helper() @@ -492,7 +525,7 @@ func (m *MockManager) FindExistingPostureCheck(accountID string, checks *posture } // FindExistingPostureCheck indicates an expected call of FindExistingPostureCheck. -func (mr *MockManagerMockRecorder) FindExistingPostureCheck(accountID, checks interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) FindExistingPostureCheck(accountID, checks any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindExistingPostureCheck", reflect.TypeOf((*MockManager)(nil).FindExistingPostureCheck), accountID, checks) } @@ -507,7 +540,7 @@ func (m *MockManager) GetAccount(ctx context.Context, accountID string) (*types. } // GetAccount indicates an expected call of GetAccount. -func (mr *MockManagerMockRecorder) GetAccount(ctx, accountID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetAccount(ctx, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockManager)(nil).GetAccount), ctx, accountID) } @@ -522,7 +555,7 @@ func (m *MockManager) GetAccountByID(ctx context.Context, accountID, userID stri } // GetAccountByID indicates an expected call of GetAccountByID. -func (mr *MockManagerMockRecorder) GetAccountByID(ctx, accountID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetAccountByID(ctx, accountID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountByID", reflect.TypeOf((*MockManager)(nil).GetAccountByID), ctx, accountID, userID) } @@ -537,7 +570,7 @@ func (m *MockManager) GetAccountIDByUserID(ctx context.Context, userAuth auth.Us } // GetAccountIDByUserID indicates an expected call of GetAccountIDByUserID. -func (mr *MockManagerMockRecorder) GetAccountIDByUserID(ctx, userAuth interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetAccountIDByUserID(ctx, userAuth any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountIDByUserID", reflect.TypeOf((*MockManager)(nil).GetAccountIDByUserID), ctx, userAuth) } @@ -552,7 +585,7 @@ func (m *MockManager) GetAccountIDForPeerKey(ctx context.Context, peerKey string } // GetAccountIDForPeerKey indicates an expected call of GetAccountIDForPeerKey. -func (mr *MockManagerMockRecorder) GetAccountIDForPeerKey(ctx, peerKey interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetAccountIDForPeerKey(ctx, peerKey any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountIDForPeerKey", reflect.TypeOf((*MockManager)(nil).GetAccountIDForPeerKey), ctx, peerKey) } @@ -568,7 +601,7 @@ func (m *MockManager) GetAccountIDFromUserAuth(ctx context.Context, userAuth aut } // GetAccountIDFromUserAuth indicates an expected call of GetAccountIDFromUserAuth. -func (mr *MockManagerMockRecorder) GetAccountIDFromUserAuth(ctx, userAuth interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetAccountIDFromUserAuth(ctx, userAuth any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountIDFromUserAuth", reflect.TypeOf((*MockManager)(nil).GetAccountIDFromUserAuth), ctx, userAuth) } @@ -583,7 +616,7 @@ func (m *MockManager) GetAccountMeta(ctx context.Context, accountID, userID stri } // GetAccountMeta indicates an expected call of GetAccountMeta. -func (mr *MockManagerMockRecorder) GetAccountMeta(ctx, accountID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetAccountMeta(ctx, accountID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountMeta", reflect.TypeOf((*MockManager)(nil).GetAccountMeta), ctx, accountID, userID) } @@ -598,7 +631,7 @@ func (m *MockManager) GetAccountOnboarding(ctx context.Context, accountID, userI } // GetAccountOnboarding indicates an expected call of GetAccountOnboarding. -func (mr *MockManagerMockRecorder) GetAccountOnboarding(ctx, accountID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetAccountOnboarding(ctx, accountID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountOnboarding", reflect.TypeOf((*MockManager)(nil).GetAccountOnboarding), ctx, accountID, userID) } @@ -613,7 +646,7 @@ func (m *MockManager) GetAccountSettings(ctx context.Context, accountID, userID } // GetAccountSettings indicates an expected call of GetAccountSettings. -func (mr *MockManagerMockRecorder) GetAccountSettings(ctx, accountID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetAccountSettings(ctx, accountID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountSettings", reflect.TypeOf((*MockManager)(nil).GetAccountSettings), ctx, accountID, userID) } @@ -628,7 +661,7 @@ func (m *MockManager) GetAllGroups(ctx context.Context, accountID, userID string } // GetAllGroups indicates an expected call of GetAllGroups. -func (mr *MockManagerMockRecorder) GetAllGroups(ctx, accountID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetAllGroups(ctx, accountID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllGroups", reflect.TypeOf((*MockManager)(nil).GetAllGroups), ctx, accountID, userID) } @@ -643,7 +676,7 @@ func (m *MockManager) GetAllPATs(ctx context.Context, accountID, initiatorUserID } // GetAllPATs indicates an expected call of GetAllPATs. -func (mr *MockManagerMockRecorder) GetAllPATs(ctx, accountID, initiatorUserID, targetUserID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetAllPATs(ctx, accountID, initiatorUserID, targetUserID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllPATs", reflect.TypeOf((*MockManager)(nil).GetAllPATs), ctx, accountID, initiatorUserID, targetUserID) } @@ -658,7 +691,7 @@ func (m *MockManager) GetAllPeerJobs(ctx context.Context, accountID, userID, pee } // GetAllPeerJobs indicates an expected call of GetAllPeerJobs. -func (mr *MockManagerMockRecorder) GetAllPeerJobs(ctx, accountID, userID, peerID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetAllPeerJobs(ctx, accountID, userID, peerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllPeerJobs", reflect.TypeOf((*MockManager)(nil).GetAllPeerJobs), ctx, accountID, userID, peerID) } @@ -673,7 +706,7 @@ func (m *MockManager) GetCurrentUserInfo(ctx context.Context, userAuth auth.User } // GetCurrentUserInfo indicates an expected call of GetCurrentUserInfo. -func (mr *MockManagerMockRecorder) GetCurrentUserInfo(ctx, userAuth interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetCurrentUserInfo(ctx, userAuth any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentUserInfo", reflect.TypeOf((*MockManager)(nil).GetCurrentUserInfo), ctx, userAuth) } @@ -688,7 +721,7 @@ func (m *MockManager) GetDNSSettings(ctx context.Context, accountID, userID stri } // GetDNSSettings indicates an expected call of GetDNSSettings. -func (mr *MockManagerMockRecorder) GetDNSSettings(ctx, accountID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetDNSSettings(ctx, accountID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDNSSettings", reflect.TypeOf((*MockManager)(nil).GetDNSSettings), ctx, accountID, userID) } @@ -703,7 +736,7 @@ func (m *MockManager) GetEvents(ctx context.Context, accountID, userID string) ( } // GetEvents indicates an expected call of GetEvents. -func (mr *MockManagerMockRecorder) GetEvents(ctx, accountID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetEvents(ctx, accountID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEvents", reflect.TypeOf((*MockManager)(nil).GetEvents), ctx, accountID, userID) } @@ -732,7 +765,7 @@ func (m *MockManager) GetGroup(ctx context.Context, accountId, groupID, userID s } // GetGroup indicates an expected call of GetGroup. -func (mr *MockManagerMockRecorder) GetGroup(ctx, accountId, groupID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetGroup(ctx, accountId, groupID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGroup", reflect.TypeOf((*MockManager)(nil).GetGroup), ctx, accountId, groupID, userID) } @@ -747,7 +780,7 @@ func (m *MockManager) GetGroupByName(ctx context.Context, groupName, accountID, } // GetGroupByName indicates an expected call of GetGroupByName. -func (mr *MockManagerMockRecorder) GetGroupByName(ctx, groupName, accountID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetGroupByName(ctx, groupName, accountID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGroupByName", reflect.TypeOf((*MockManager)(nil).GetGroupByName), ctx, groupName, accountID, userID) } @@ -762,7 +795,7 @@ func (m *MockManager) GetIdentityProvider(ctx context.Context, accountID, idpID, } // GetIdentityProvider indicates an expected call of GetIdentityProvider. -func (mr *MockManagerMockRecorder) GetIdentityProvider(ctx, accountID, idpID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetIdentityProvider(ctx, accountID, idpID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIdentityProvider", reflect.TypeOf((*MockManager)(nil).GetIdentityProvider), ctx, accountID, idpID, userID) } @@ -777,7 +810,7 @@ func (m *MockManager) GetIdentityProviders(ctx context.Context, accountID, userI } // GetIdentityProviders indicates an expected call of GetIdentityProviders. -func (mr *MockManagerMockRecorder) GetIdentityProviders(ctx, accountID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetIdentityProviders(ctx, accountID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIdentityProviders", reflect.TypeOf((*MockManager)(nil).GetIdentityProviders), ctx, accountID, userID) } @@ -806,7 +839,7 @@ func (m *MockManager) GetNameServerGroup(ctx context.Context, accountID, userID, } // GetNameServerGroup indicates an expected call of GetNameServerGroup. -func (mr *MockManagerMockRecorder) GetNameServerGroup(ctx, accountID, userID, nsGroupID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetNameServerGroup(ctx, accountID, userID, nsGroupID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNameServerGroup", reflect.TypeOf((*MockManager)(nil).GetNameServerGroup), ctx, accountID, userID, nsGroupID) } @@ -821,15 +854,15 @@ func (m *MockManager) GetNetworkMap(ctx context.Context, peerID string) (*types. } // GetNetworkMap indicates an expected call of GetNetworkMap. -func (mr *MockManagerMockRecorder) GetNetworkMap(ctx, peerID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetNetworkMap(ctx, peerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetworkMap", reflect.TypeOf((*MockManager)(nil).GetNetworkMap), ctx, peerID) } // GetOrCreateAccountByPrivateDomain mocks base method. -func (m *MockManager) GetOrCreateAccountByPrivateDomain(ctx context.Context, initiatorId, domain string) (*types.Account, bool, error) { +func (m *MockManager) GetOrCreateAccountByPrivateDomain(ctx context.Context, initiatorId, arg2 string) (*types.Account, bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetOrCreateAccountByPrivateDomain", ctx, initiatorId, domain) + ret := m.ctrl.Call(m, "GetOrCreateAccountByPrivateDomain", ctx, initiatorId, arg2) ret0, _ := ret[0].(*types.Account) ret1, _ := ret[1].(bool) ret2, _ := ret[2].(error) @@ -837,9 +870,9 @@ func (m *MockManager) GetOrCreateAccountByPrivateDomain(ctx context.Context, ini } // GetOrCreateAccountByPrivateDomain indicates an expected call of GetOrCreateAccountByPrivateDomain. -func (mr *MockManagerMockRecorder) GetOrCreateAccountByPrivateDomain(ctx, initiatorId, domain interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetOrCreateAccountByPrivateDomain(ctx, initiatorId, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrCreateAccountByPrivateDomain", reflect.TypeOf((*MockManager)(nil).GetOrCreateAccountByPrivateDomain), ctx, initiatorId, domain) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrCreateAccountByPrivateDomain", reflect.TypeOf((*MockManager)(nil).GetOrCreateAccountByPrivateDomain), ctx, initiatorId, arg2) } // GetOrCreateAccountByUser mocks base method. @@ -852,7 +885,7 @@ func (m *MockManager) GetOrCreateAccountByUser(ctx context.Context, userAuth aut } // GetOrCreateAccountByUser indicates an expected call of GetOrCreateAccountByUser. -func (mr *MockManagerMockRecorder) GetOrCreateAccountByUser(ctx, userAuth interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetOrCreateAccountByUser(ctx, userAuth any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrCreateAccountByUser", reflect.TypeOf((*MockManager)(nil).GetOrCreateAccountByUser), ctx, userAuth) } @@ -867,7 +900,7 @@ func (m *MockManager) GetOwnerInfo(ctx context.Context, accountId string) (*type } // GetOwnerInfo indicates an expected call of GetOwnerInfo. -func (mr *MockManagerMockRecorder) GetOwnerInfo(ctx, accountId interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetOwnerInfo(ctx, accountId any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOwnerInfo", reflect.TypeOf((*MockManager)(nil).GetOwnerInfo), ctx, accountId) } @@ -882,7 +915,7 @@ func (m *MockManager) GetPAT(ctx context.Context, accountID, initiatorUserID, ta } // GetPAT indicates an expected call of GetPAT. -func (mr *MockManagerMockRecorder) GetPAT(ctx, accountID, initiatorUserID, targetUserID, tokenID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetPAT(ctx, accountID, initiatorUserID, targetUserID, tokenID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPAT", reflect.TypeOf((*MockManager)(nil).GetPAT), ctx, accountID, initiatorUserID, targetUserID, tokenID) } @@ -897,7 +930,7 @@ func (m *MockManager) GetPeer(ctx context.Context, accountID, peerID, userID str } // GetPeer indicates an expected call of GetPeer. -func (mr *MockManagerMockRecorder) GetPeer(ctx, accountID, peerID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetPeer(ctx, accountID, peerID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeer", reflect.TypeOf((*MockManager)(nil).GetPeer), ctx, accountID, peerID, userID) } @@ -912,7 +945,7 @@ func (m *MockManager) GetPeerGroups(ctx context.Context, accountID, peerID strin } // GetPeerGroups indicates an expected call of GetPeerGroups. -func (mr *MockManagerMockRecorder) GetPeerGroups(ctx, accountID, peerID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetPeerGroups(ctx, accountID, peerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerGroups", reflect.TypeOf((*MockManager)(nil).GetPeerGroups), ctx, accountID, peerID) } @@ -927,7 +960,7 @@ func (m *MockManager) GetPeerJobByID(ctx context.Context, accountID, userID, pee } // GetPeerJobByID indicates an expected call of GetPeerJobByID. -func (mr *MockManagerMockRecorder) GetPeerJobByID(ctx, accountID, userID, peerID, jobID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetPeerJobByID(ctx, accountID, userID, peerID, jobID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerJobByID", reflect.TypeOf((*MockManager)(nil).GetPeerJobByID), ctx, accountID, userID, peerID, jobID) } @@ -942,7 +975,7 @@ func (m *MockManager) GetPeerNetwork(ctx context.Context, peerID string) (*types } // GetPeerNetwork indicates an expected call of GetPeerNetwork. -func (mr *MockManagerMockRecorder) GetPeerNetwork(ctx, peerID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetPeerNetwork(ctx, peerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerNetwork", reflect.TypeOf((*MockManager)(nil).GetPeerNetwork), ctx, peerID) } @@ -957,7 +990,7 @@ func (m *MockManager) GetPeers(ctx context.Context, accountID, userID, nameFilte } // GetPeers indicates an expected call of GetPeers. -func (mr *MockManagerMockRecorder) GetPeers(ctx, accountID, userID, nameFilter, ipFilter interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetPeers(ctx, accountID, userID, nameFilter, ipFilter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeers", reflect.TypeOf((*MockManager)(nil).GetPeers), ctx, accountID, userID, nameFilter, ipFilter) } @@ -972,7 +1005,7 @@ func (m *MockManager) GetPolicy(ctx context.Context, accountID, policyID, userID } // GetPolicy indicates an expected call of GetPolicy. -func (mr *MockManagerMockRecorder) GetPolicy(ctx, accountID, policyID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetPolicy(ctx, accountID, policyID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPolicy", reflect.TypeOf((*MockManager)(nil).GetPolicy), ctx, accountID, policyID, userID) } @@ -987,7 +1020,7 @@ func (m *MockManager) GetPostureChecks(ctx context.Context, accountID, postureCh } // GetPostureChecks indicates an expected call of GetPostureChecks. -func (mr *MockManagerMockRecorder) GetPostureChecks(ctx, accountID, postureChecksID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetPostureChecks(ctx, accountID, postureChecksID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPostureChecks", reflect.TypeOf((*MockManager)(nil).GetPostureChecks), ctx, accountID, postureChecksID, userID) } @@ -1002,7 +1035,7 @@ func (m *MockManager) GetRoute(ctx context.Context, accountID string, routeID ro } // GetRoute indicates an expected call of GetRoute. -func (mr *MockManagerMockRecorder) GetRoute(ctx, accountID, routeID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetRoute(ctx, accountID, routeID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRoute", reflect.TypeOf((*MockManager)(nil).GetRoute), ctx, accountID, routeID, userID) } @@ -1017,7 +1050,7 @@ func (m *MockManager) GetSetupKey(ctx context.Context, accountID, userID, keyID } // GetSetupKey indicates an expected call of GetSetupKey. -func (mr *MockManagerMockRecorder) GetSetupKey(ctx, accountID, userID, keyID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetSetupKey(ctx, accountID, userID, keyID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSetupKey", reflect.TypeOf((*MockManager)(nil).GetSetupKey), ctx, accountID, userID, keyID) } @@ -1046,7 +1079,7 @@ func (m *MockManager) GetUserByID(ctx context.Context, id string) (*types.User, } // GetUserByID indicates an expected call of GetUserByID. -func (mr *MockManagerMockRecorder) GetUserByID(ctx, id interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetUserByID(ctx, id any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserByID", reflect.TypeOf((*MockManager)(nil).GetUserByID), ctx, id) } @@ -1061,7 +1094,7 @@ func (m *MockManager) GetUserFromUserAuth(ctx context.Context, userAuth auth.Use } // GetUserFromUserAuth indicates an expected call of GetUserFromUserAuth. -func (mr *MockManagerMockRecorder) GetUserFromUserAuth(ctx, userAuth interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetUserFromUserAuth(ctx, userAuth any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserFromUserAuth", reflect.TypeOf((*MockManager)(nil).GetUserFromUserAuth), ctx, userAuth) } @@ -1076,7 +1109,7 @@ func (m *MockManager) GetUserIDByPeerKey(ctx context.Context, peerKey string) (s } // GetUserIDByPeerKey indicates an expected call of GetUserIDByPeerKey. -func (mr *MockManagerMockRecorder) GetUserIDByPeerKey(ctx, peerKey interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetUserIDByPeerKey(ctx, peerKey any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserIDByPeerKey", reflect.TypeOf((*MockManager)(nil).GetUserIDByPeerKey), ctx, peerKey) } @@ -1091,7 +1124,7 @@ func (m *MockManager) GetUserInviteInfo(ctx context.Context, token string) (*typ } // GetUserInviteInfo indicates an expected call of GetUserInviteInfo. -func (mr *MockManagerMockRecorder) GetUserInviteInfo(ctx, token interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetUserInviteInfo(ctx, token any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserInviteInfo", reflect.TypeOf((*MockManager)(nil).GetUserInviteInfo), ctx, token) } @@ -1106,7 +1139,7 @@ func (m *MockManager) GetUsersFromAccount(ctx context.Context, accountID, userID } // GetUsersFromAccount indicates an expected call of GetUsersFromAccount. -func (mr *MockManagerMockRecorder) GetUsersFromAccount(ctx, accountID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetUsersFromAccount(ctx, accountID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUsersFromAccount", reflect.TypeOf((*MockManager)(nil).GetUsersFromAccount), ctx, accountID, userID) } @@ -1122,7 +1155,7 @@ func (m *MockManager) GetValidatedPeers(ctx context.Context, accountID string) ( } // GetValidatedPeers indicates an expected call of GetValidatedPeers. -func (mr *MockManagerMockRecorder) GetValidatedPeers(ctx, accountID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetValidatedPeers(ctx, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatedPeers", reflect.TypeOf((*MockManager)(nil).GetValidatedPeers), ctx, accountID) } @@ -1136,7 +1169,7 @@ func (m *MockManager) GroupAddPeer(ctx context.Context, accountId, groupID, peer } // GroupAddPeer indicates an expected call of GroupAddPeer. -func (mr *MockManagerMockRecorder) GroupAddPeer(ctx, accountId, groupID, peerID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GroupAddPeer(ctx, accountId, groupID, peerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GroupAddPeer", reflect.TypeOf((*MockManager)(nil).GroupAddPeer), ctx, accountId, groupID, peerID) } @@ -1150,7 +1183,7 @@ func (m *MockManager) GroupDeletePeer(ctx context.Context, accountId, groupID, p } // GroupDeletePeer indicates an expected call of GroupDeletePeer. -func (mr *MockManagerMockRecorder) GroupDeletePeer(ctx, accountId, groupID, peerID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GroupDeletePeer(ctx, accountId, groupID, peerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GroupDeletePeer", reflect.TypeOf((*MockManager)(nil).GroupDeletePeer), ctx, accountId, groupID, peerID) } @@ -1165,7 +1198,7 @@ func (m *MockManager) GroupValidation(ctx context.Context, accountId string, gro } // GroupValidation indicates an expected call of GroupValidation. -func (mr *MockManagerMockRecorder) GroupValidation(ctx, accountId, groups interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GroupValidation(ctx, accountId, groups any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GroupValidation", reflect.TypeOf((*MockManager)(nil).GroupValidation), ctx, accountId, groups) } @@ -1179,7 +1212,7 @@ func (m *MockManager) InviteUser(ctx context.Context, accountID, initiatorUserID } // InviteUser indicates an expected call of InviteUser. -func (mr *MockManagerMockRecorder) InviteUser(ctx, accountID, initiatorUserID, targetUserID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) InviteUser(ctx, accountID, initiatorUserID, targetUserID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InviteUser", reflect.TypeOf((*MockManager)(nil).InviteUser), ctx, accountID, initiatorUserID, targetUserID) } @@ -1194,7 +1227,7 @@ func (m *MockManager) ListNameServerGroups(ctx context.Context, accountID, userI } // ListNameServerGroups indicates an expected call of ListNameServerGroups. -func (mr *MockManagerMockRecorder) ListNameServerGroups(ctx, accountID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) ListNameServerGroups(ctx, accountID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListNameServerGroups", reflect.TypeOf((*MockManager)(nil).ListNameServerGroups), ctx, accountID, userID) } @@ -1209,7 +1242,7 @@ func (m *MockManager) ListPolicies(ctx context.Context, accountID, userID string } // ListPolicies indicates an expected call of ListPolicies. -func (mr *MockManagerMockRecorder) ListPolicies(ctx, accountID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) ListPolicies(ctx, accountID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListPolicies", reflect.TypeOf((*MockManager)(nil).ListPolicies), ctx, accountID, userID) } @@ -1224,7 +1257,7 @@ func (m *MockManager) ListPostureChecks(ctx context.Context, accountID, userID s } // ListPostureChecks indicates an expected call of ListPostureChecks. -func (mr *MockManagerMockRecorder) ListPostureChecks(ctx, accountID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) ListPostureChecks(ctx, accountID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListPostureChecks", reflect.TypeOf((*MockManager)(nil).ListPostureChecks), ctx, accountID, userID) } @@ -1239,7 +1272,7 @@ func (m *MockManager) ListRoutes(ctx context.Context, accountID, userID string) } // ListRoutes indicates an expected call of ListRoutes. -func (mr *MockManagerMockRecorder) ListRoutes(ctx, accountID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) ListRoutes(ctx, accountID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListRoutes", reflect.TypeOf((*MockManager)(nil).ListRoutes), ctx, accountID, userID) } @@ -1254,7 +1287,7 @@ func (m *MockManager) ListSetupKeys(ctx context.Context, accountID, userID strin } // ListSetupKeys indicates an expected call of ListSetupKeys. -func (mr *MockManagerMockRecorder) ListSetupKeys(ctx, accountID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) ListSetupKeys(ctx, accountID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListSetupKeys", reflect.TypeOf((*MockManager)(nil).ListSetupKeys), ctx, accountID, userID) } @@ -1269,7 +1302,7 @@ func (m *MockManager) ListUserInvites(ctx context.Context, accountID, initiatorU } // ListUserInvites indicates an expected call of ListUserInvites. -func (mr *MockManagerMockRecorder) ListUserInvites(ctx, accountID, initiatorUserID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) ListUserInvites(ctx, accountID, initiatorUserID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListUserInvites", reflect.TypeOf((*MockManager)(nil).ListUserInvites), ctx, accountID, initiatorUserID) } @@ -1284,7 +1317,7 @@ func (m *MockManager) ListUsers(ctx context.Context, accountID string) ([]*types } // ListUsers indicates an expected call of ListUsers. -func (mr *MockManagerMockRecorder) ListUsers(ctx, accountID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) ListUsers(ctx, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListUsers", reflect.TypeOf((*MockManager)(nil).ListUsers), ctx, accountID) } @@ -1302,28 +1335,13 @@ func (m *MockManager) LoginPeer(ctx context.Context, login types.PeerLogin) (*pe } // LoginPeer indicates an expected call of LoginPeer. -func (mr *MockManagerMockRecorder) LoginPeer(ctx, login interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) LoginPeer(ctx, login any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoginPeer", reflect.TypeOf((*MockManager)(nil).LoginPeer), ctx, login) } -// ExtendPeerSession mocks base method. -func (m *MockManager) ExtendPeerSession(ctx context.Context, peerPubKey, userID string) (time.Time, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ExtendPeerSession", ctx, peerPubKey, userID) - ret0, _ := ret[0].(time.Time) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ExtendPeerSession indicates an expected call of ExtendPeerSession. -func (mr *MockManagerMockRecorder) ExtendPeerSession(ctx, peerPubKey, userID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExtendPeerSession", reflect.TypeOf((*MockManager)(nil).ExtendPeerSession), ctx, peerPubKey, userID) -} - // MarkPeerConnected mocks base method. -func (m *MockManager) MarkPeerConnected(ctx context.Context, peerKey string, accountID string, sessionStartedAt int64, nmap *types.NetworkMap) error { +func (m *MockManager) MarkPeerConnected(ctx context.Context, peerKey, accountID string, sessionStartedAt int64, nmap *types.NetworkMap) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MarkPeerConnected", ctx, peerKey, accountID, sessionStartedAt, nmap) ret0, _ := ret[0].(error) @@ -1331,13 +1349,13 @@ func (m *MockManager) MarkPeerConnected(ctx context.Context, peerKey string, acc } // MarkPeerConnected indicates an expected call of MarkPeerConnected. -func (mr *MockManagerMockRecorder) MarkPeerConnected(ctx, peerKey, accountID, sessionStartedAt, nmap interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) MarkPeerConnected(ctx, peerKey, accountID, sessionStartedAt, nmap any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MarkPeerConnected", reflect.TypeOf((*MockManager)(nil).MarkPeerConnected), ctx, peerKey, accountID, sessionStartedAt, nmap) } // MarkPeerDisconnected mocks base method. -func (m *MockManager) MarkPeerDisconnected(ctx context.Context, peerKey string, accountID string, sessionStartedAt int64) error { +func (m *MockManager) MarkPeerDisconnected(ctx context.Context, peerKey, accountID string, sessionStartedAt int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MarkPeerDisconnected", ctx, peerKey, accountID, sessionStartedAt) ret0, _ := ret[0].(error) @@ -1345,7 +1363,7 @@ func (m *MockManager) MarkPeerDisconnected(ctx context.Context, peerKey string, } // MarkPeerDisconnected indicates an expected call of MarkPeerDisconnected. -func (mr *MockManagerMockRecorder) MarkPeerDisconnected(ctx, peerKey, accountID, sessionStartedAt interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) MarkPeerDisconnected(ctx, peerKey, accountID, sessionStartedAt any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MarkPeerDisconnected", reflect.TypeOf((*MockManager)(nil).MarkPeerDisconnected), ctx, peerKey, accountID, sessionStartedAt) } @@ -1359,7 +1377,7 @@ func (m *MockManager) OnPeerDisconnected(ctx context.Context, accountID, peerPub } // OnPeerDisconnected indicates an expected call of OnPeerDisconnected. -func (mr *MockManagerMockRecorder) OnPeerDisconnected(ctx, accountID, peerPubKey, streamStartTime interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) OnPeerDisconnected(ctx, accountID, peerPubKey, streamStartTime any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnPeerDisconnected", reflect.TypeOf((*MockManager)(nil).OnPeerDisconnected), ctx, accountID, peerPubKey, streamStartTime) } @@ -1374,7 +1392,7 @@ func (m *MockManager) RegenerateUserInvite(ctx context.Context, accountID, initi } // RegenerateUserInvite indicates an expected call of RegenerateUserInvite. -func (mr *MockManagerMockRecorder) RegenerateUserInvite(ctx, accountID, initiatorUserID, inviteID, expiresIn interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) RegenerateUserInvite(ctx, accountID, initiatorUserID, inviteID, expiresIn any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegenerateUserInvite", reflect.TypeOf((*MockManager)(nil).RegenerateUserInvite), ctx, accountID, initiatorUserID, inviteID, expiresIn) } @@ -1388,7 +1406,7 @@ func (m *MockManager) RejectUser(ctx context.Context, accountID, initiatorUserID } // RejectUser indicates an expected call of RejectUser. -func (mr *MockManagerMockRecorder) RejectUser(ctx, accountID, initiatorUserID, targetUserID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) RejectUser(ctx, accountID, initiatorUserID, targetUserID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RejectUser", reflect.TypeOf((*MockManager)(nil).RejectUser), ctx, accountID, initiatorUserID, targetUserID) } @@ -1402,7 +1420,7 @@ func (m *MockManager) SaveDNSSettings(ctx context.Context, accountID, userID str } // SaveDNSSettings indicates an expected call of SaveDNSSettings. -func (mr *MockManagerMockRecorder) SaveDNSSettings(ctx, accountID, userID, dnsSettingsToSave interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) SaveDNSSettings(ctx, accountID, userID, dnsSettingsToSave any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveDNSSettings", reflect.TypeOf((*MockManager)(nil).SaveDNSSettings), ctx, accountID, userID, dnsSettingsToSave) } @@ -1416,7 +1434,7 @@ func (m *MockManager) SaveNameServerGroup(ctx context.Context, accountID, userID } // SaveNameServerGroup indicates an expected call of SaveNameServerGroup. -func (mr *MockManagerMockRecorder) SaveNameServerGroup(ctx, accountID, userID, nsGroupToSave interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) SaveNameServerGroup(ctx, accountID, userID, nsGroupToSave any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveNameServerGroup", reflect.TypeOf((*MockManager)(nil).SaveNameServerGroup), ctx, accountID, userID, nsGroupToSave) } @@ -1431,7 +1449,7 @@ func (m *MockManager) SaveOrAddUser(ctx context.Context, accountID, initiatorUse } // SaveOrAddUser indicates an expected call of SaveOrAddUser. -func (mr *MockManagerMockRecorder) SaveOrAddUser(ctx, accountID, initiatorUserID, update, addIfNotExists interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) SaveOrAddUser(ctx, accountID, initiatorUserID, update, addIfNotExists any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveOrAddUser", reflect.TypeOf((*MockManager)(nil).SaveOrAddUser), ctx, accountID, initiatorUserID, update, addIfNotExists) } @@ -1446,7 +1464,7 @@ func (m *MockManager) SaveOrAddUsers(ctx context.Context, accountID, initiatorUs } // SaveOrAddUsers indicates an expected call of SaveOrAddUsers. -func (mr *MockManagerMockRecorder) SaveOrAddUsers(ctx, accountID, initiatorUserID, updates, addIfNotExists interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) SaveOrAddUsers(ctx, accountID, initiatorUserID, updates, addIfNotExists any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveOrAddUsers", reflect.TypeOf((*MockManager)(nil).SaveOrAddUsers), ctx, accountID, initiatorUserID, updates, addIfNotExists) } @@ -1461,7 +1479,7 @@ func (m *MockManager) SavePolicy(ctx context.Context, accountID, userID string, } // SavePolicy indicates an expected call of SavePolicy. -func (mr *MockManagerMockRecorder) SavePolicy(ctx, accountID, userID, policy, create interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) SavePolicy(ctx, accountID, userID, policy, create any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SavePolicy", reflect.TypeOf((*MockManager)(nil).SavePolicy), ctx, accountID, userID, policy, create) } @@ -1476,23 +1494,23 @@ func (m *MockManager) SavePostureChecks(ctx context.Context, accountID, userID s } // SavePostureChecks indicates an expected call of SavePostureChecks. -func (mr *MockManagerMockRecorder) SavePostureChecks(ctx, accountID, userID, postureChecks, create interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) SavePostureChecks(ctx, accountID, userID, postureChecks, create any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SavePostureChecks", reflect.TypeOf((*MockManager)(nil).SavePostureChecks), ctx, accountID, userID, postureChecks, create) } // SaveRoute mocks base method. -func (m *MockManager) SaveRoute(ctx context.Context, accountID, userID string, route *route.Route) error { +func (m *MockManager) SaveRoute(ctx context.Context, accountID, userID string, arg3 *route.Route) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SaveRoute", ctx, accountID, userID, route) + ret := m.ctrl.Call(m, "SaveRoute", ctx, accountID, userID, arg3) ret0, _ := ret[0].(error) return ret0 } // SaveRoute indicates an expected call of SaveRoute. -func (mr *MockManagerMockRecorder) SaveRoute(ctx, accountID, userID, route interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) SaveRoute(ctx, accountID, userID, arg3 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveRoute", reflect.TypeOf((*MockManager)(nil).SaveRoute), ctx, accountID, userID, route) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveRoute", reflect.TypeOf((*MockManager)(nil).SaveRoute), ctx, accountID, userID, arg3) } // SaveSetupKey mocks base method. @@ -1505,7 +1523,7 @@ func (m *MockManager) SaveSetupKey(ctx context.Context, accountID string, key *t } // SaveSetupKey indicates an expected call of SaveSetupKey. -func (mr *MockManagerMockRecorder) SaveSetupKey(ctx, accountID, key, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) SaveSetupKey(ctx, accountID, key, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveSetupKey", reflect.TypeOf((*MockManager)(nil).SaveSetupKey), ctx, accountID, key, userID) } @@ -1520,7 +1538,7 @@ func (m *MockManager) SaveUser(ctx context.Context, accountID, initiatorUserID s } // SaveUser indicates an expected call of SaveUser. -func (mr *MockManagerMockRecorder) SaveUser(ctx, accountID, initiatorUserID, update interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) SaveUser(ctx, accountID, initiatorUserID, update any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveUser", reflect.TypeOf((*MockManager)(nil).SaveUser), ctx, accountID, initiatorUserID, update) } @@ -1532,7 +1550,7 @@ func (m *MockManager) SetServiceManager(serviceManager service.Manager) { } // SetServiceManager indicates an expected call of SetServiceManager. -func (mr *MockManagerMockRecorder) SetServiceManager(serviceManager interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) SetServiceManager(serviceManager any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetServiceManager", reflect.TypeOf((*MockManager)(nil).SetServiceManager), serviceManager) } @@ -1544,7 +1562,7 @@ func (m *MockManager) StoreEvent(ctx context.Context, initiatorID, targetID, acc } // StoreEvent indicates an expected call of StoreEvent. -func (mr *MockManagerMockRecorder) StoreEvent(ctx, initiatorID, targetID, accountID, activityID, meta interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) StoreEvent(ctx, initiatorID, targetID, accountID, activityID, meta any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StoreEvent", reflect.TypeOf((*MockManager)(nil).StoreEvent), ctx, initiatorID, targetID, accountID, activityID, meta) } @@ -1562,7 +1580,7 @@ func (m *MockManager) SyncAndMarkPeer(ctx context.Context, accountID, peerPubKey } // SyncAndMarkPeer indicates an expected call of SyncAndMarkPeer. -func (mr *MockManagerMockRecorder) SyncAndMarkPeer(ctx, accountID, peerPubKey, meta, realIP, syncTime interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) SyncAndMarkPeer(ctx, accountID, peerPubKey, meta, realIP, syncTime any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncAndMarkPeer", reflect.TypeOf((*MockManager)(nil).SyncAndMarkPeer), ctx, accountID, peerPubKey, meta, realIP, syncTime) } @@ -1580,7 +1598,7 @@ func (m *MockManager) SyncPeer(ctx context.Context, sync types.PeerSync, account } // SyncPeer indicates an expected call of SyncPeer. -func (mr *MockManagerMockRecorder) SyncPeer(ctx, sync, accountID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) SyncPeer(ctx, sync, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncPeer", reflect.TypeOf((*MockManager)(nil).SyncPeer), ctx, sync, accountID) } @@ -1594,7 +1612,7 @@ func (m *MockManager) SyncPeerMeta(ctx context.Context, peerPubKey string, meta } // SyncPeerMeta indicates an expected call of SyncPeerMeta. -func (mr *MockManagerMockRecorder) SyncPeerMeta(ctx, peerPubKey, meta, realIP interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) SyncPeerMeta(ctx, peerPubKey, meta, realIP any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncPeerMeta", reflect.TypeOf((*MockManager)(nil).SyncPeerMeta), ctx, peerPubKey, meta, realIP) } @@ -1608,7 +1626,7 @@ func (m *MockManager) SyncUserJWTGroups(ctx context.Context, userAuth auth.UserA } // SyncUserJWTGroups indicates an expected call of SyncUserJWTGroups. -func (mr *MockManagerMockRecorder) SyncUserJWTGroups(ctx, userAuth interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) SyncUserJWTGroups(ctx, userAuth any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncUserJWTGroups", reflect.TypeOf((*MockManager)(nil).SyncUserJWTGroups), ctx, userAuth) } @@ -1623,7 +1641,7 @@ func (m *MockManager) UpdateAccountOnboarding(ctx context.Context, accountID, us } // UpdateAccountOnboarding indicates an expected call of UpdateAccountOnboarding. -func (mr *MockManagerMockRecorder) UpdateAccountOnboarding(ctx, accountID, userID, newOnboarding interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) UpdateAccountOnboarding(ctx, accountID, userID, newOnboarding any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAccountOnboarding", reflect.TypeOf((*MockManager)(nil).UpdateAccountOnboarding), ctx, accountID, userID, newOnboarding) } @@ -1635,23 +1653,11 @@ func (m *MockManager) UpdateAccountPeers(ctx context.Context, accountID string, } // UpdateAccountPeers indicates an expected call of UpdateAccountPeers. -func (mr *MockManagerMockRecorder) UpdateAccountPeers(ctx, accountID, reason interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) UpdateAccountPeers(ctx, accountID, reason any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAccountPeers", reflect.TypeOf((*MockManager)(nil).UpdateAccountPeers), ctx, accountID, reason) } -// ExpandAndUpdateAffected mocks base method. -func (m *MockManager) ExpandAndUpdateAffected(ctx context.Context, accountID string, snap *affectedpeers.Snapshot, change affectedpeers.Change) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "ExpandAndUpdateAffected", ctx, accountID, snap, change) -} - -// ExpandAndUpdateAffected indicates an expected call of ExpandAndUpdateAffected. -func (mr *MockManagerMockRecorder) ExpandAndUpdateAffected(ctx, accountID, snap, change interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExpandAndUpdateAffected", reflect.TypeOf((*MockManager)(nil).ExpandAndUpdateAffected), ctx, accountID, snap, change) -} - // UpdateAccountSettings mocks base method. func (m *MockManager) UpdateAccountSettings(ctx context.Context, accountID, userID string, newSettings *types.Settings) (*types.Settings, error) { m.ctrl.T.Helper() @@ -1662,7 +1668,7 @@ func (m *MockManager) UpdateAccountSettings(ctx context.Context, accountID, user } // UpdateAccountSettings indicates an expected call of UpdateAccountSettings. -func (mr *MockManagerMockRecorder) UpdateAccountSettings(ctx, accountID, userID, newSettings interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) UpdateAccountSettings(ctx, accountID, userID, newSettings any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAccountSettings", reflect.TypeOf((*MockManager)(nil).UpdateAccountSettings), ctx, accountID, userID, newSettings) } @@ -1676,7 +1682,7 @@ func (m *MockManager) UpdateGroup(ctx context.Context, accountID, userID string, } // UpdateGroup indicates an expected call of UpdateGroup. -func (mr *MockManagerMockRecorder) UpdateGroup(ctx, accountID, userID, group interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) UpdateGroup(ctx, accountID, userID, group any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateGroup", reflect.TypeOf((*MockManager)(nil).UpdateGroup), ctx, accountID, userID, group) } @@ -1690,24 +1696,24 @@ func (m *MockManager) UpdateGroups(ctx context.Context, accountID, userID string } // UpdateGroups indicates an expected call of UpdateGroups. -func (mr *MockManagerMockRecorder) UpdateGroups(ctx, accountID, userID, newGroups interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) UpdateGroups(ctx, accountID, userID, newGroups any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateGroups", reflect.TypeOf((*MockManager)(nil).UpdateGroups), ctx, accountID, userID, newGroups) } // UpdateIdentityProvider mocks base method. -func (m *MockManager) UpdateIdentityProvider(ctx context.Context, accountID, idpID, userID string, idp *types.IdentityProvider) (*types.IdentityProvider, error) { +func (m *MockManager) UpdateIdentityProvider(ctx context.Context, accountID, idpID, userID string, arg4 *types.IdentityProvider) (*types.IdentityProvider, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateIdentityProvider", ctx, accountID, idpID, userID, idp) + ret := m.ctrl.Call(m, "UpdateIdentityProvider", ctx, accountID, idpID, userID, arg4) ret0, _ := ret[0].(*types.IdentityProvider) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateIdentityProvider indicates an expected call of UpdateIdentityProvider. -func (mr *MockManagerMockRecorder) UpdateIdentityProvider(ctx, accountID, idpID, userID, idp interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) UpdateIdentityProvider(ctx, accountID, idpID, userID, arg4 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateIdentityProvider", reflect.TypeOf((*MockManager)(nil).UpdateIdentityProvider), ctx, accountID, idpID, userID, idp) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateIdentityProvider", reflect.TypeOf((*MockManager)(nil).UpdateIdentityProvider), ctx, accountID, idpID, userID, arg4) } // UpdateIntegratedValidator mocks base method. @@ -1719,7 +1725,7 @@ func (m *MockManager) UpdateIntegratedValidator(ctx context.Context, accountID, } // UpdateIntegratedValidator indicates an expected call of UpdateIntegratedValidator. -func (mr *MockManagerMockRecorder) UpdateIntegratedValidator(ctx, accountID, userID, validator, groups interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) UpdateIntegratedValidator(ctx, accountID, userID, validator, groups any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateIntegratedValidator", reflect.TypeOf((*MockManager)(nil).UpdateIntegratedValidator), ctx, accountID, userID, validator, groups) } @@ -1734,7 +1740,7 @@ func (m *MockManager) UpdatePeer(ctx context.Context, accountID, userID string, } // UpdatePeer indicates an expected call of UpdatePeer. -func (mr *MockManagerMockRecorder) UpdatePeer(ctx, accountID, userID, p interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) UpdatePeer(ctx, accountID, userID, p any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatePeer", reflect.TypeOf((*MockManager)(nil).UpdatePeer), ctx, accountID, userID, p) } @@ -1748,11 +1754,12 @@ func (m *MockManager) UpdatePeerIP(ctx context.Context, accountID, userID, peerI } // UpdatePeerIP indicates an expected call of UpdatePeerIP. -func (mr *MockManagerMockRecorder) UpdatePeerIP(ctx, accountID, userID, peerID, newIP interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) UpdatePeerIP(ctx, accountID, userID, peerID, newIP any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatePeerIP", reflect.TypeOf((*MockManager)(nil).UpdatePeerIP), ctx, accountID, userID, peerID, newIP) } +// UpdatePeerIPv6 mocks base method. func (m *MockManager) UpdatePeerIPv6(ctx context.Context, accountID, userID, peerID string, newIPv6 netip.Addr) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdatePeerIPv6", ctx, accountID, userID, peerID, newIPv6) @@ -1760,7 +1767,8 @@ func (m *MockManager) UpdatePeerIPv6(ctx context.Context, accountID, userID, pee return ret0 } -func (mr *MockManagerMockRecorder) UpdatePeerIPv6(ctx, accountID, userID, peerID, newIPv6 interface{}) *gomock.Call { +// UpdatePeerIPv6 indicates an expected call of UpdatePeerIPv6. +func (mr *MockManagerMockRecorder) UpdatePeerIPv6(ctx, accountID, userID, peerID, newIPv6 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatePeerIPv6", reflect.TypeOf((*MockManager)(nil).UpdatePeerIPv6), ctx, accountID, userID, peerID, newIPv6) } @@ -1774,7 +1782,7 @@ func (m *MockManager) UpdateToPrimaryAccount(ctx context.Context, accountId stri } // UpdateToPrimaryAccount indicates an expected call of UpdateToPrimaryAccount. -func (mr *MockManagerMockRecorder) UpdateToPrimaryAccount(ctx, accountId interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) UpdateToPrimaryAccount(ctx, accountId any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateToPrimaryAccount", reflect.TypeOf((*MockManager)(nil).UpdateToPrimaryAccount), ctx, accountId) } @@ -1788,7 +1796,7 @@ func (m *MockManager) UpdateUserPassword(ctx context.Context, accountID, current } // UpdateUserPassword indicates an expected call of UpdateUserPassword. -func (mr *MockManagerMockRecorder) UpdateUserPassword(ctx, accountID, currentUserID, targetUserID, oldPassword, newPassword interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) UpdateUserPassword(ctx, accountID, currentUserID, targetUserID, oldPassword, newPassword any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateUserPassword", reflect.TypeOf((*MockManager)(nil).UpdateUserPassword), ctx, accountID, currentUserID, targetUserID, oldPassword, newPassword) } diff --git a/management/server/account_test.go b/management/server/account_test.go index 73126a496..5a826e103 100644 --- a/management/server/account_test.go +++ b/management/server/account_test.go @@ -14,7 +14,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/prometheus/client_golang/prometheus/push" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" diff --git a/management/server/dns_test.go b/management/server/dns_test.go index 8917902d9..d7667a304 100644 --- a/management/server/dns_test.go +++ b/management/server/dns_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/assert" nbdns "github.com/netbirdio/netbird/dns" diff --git a/management/server/group_test.go b/management/server/group_test.go index deeec61d5..f5aeceea8 100644 --- a/management/server/group_test.go +++ b/management/server/group_test.go @@ -11,7 +11,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/management/server/http/handlers/accounts/accounts_handler_test.go b/management/server/http/handlers/accounts/accounts_handler_test.go index 0069efcb7..06419019e 100644 --- a/management/server/http/handlers/accounts/accounts_handler_test.go +++ b/management/server/http/handlers/accounts/accounts_handler_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" diff --git a/management/server/http/handlers/instance/instance_handler_test.go b/management/server/http/handlers/instance/instance_handler_test.go index 711e01964..ba59497fa 100644 --- a/management/server/http/handlers/instance/instance_handler_test.go +++ b/management/server/http/handlers/instance/instance_handler_test.go @@ -10,7 +10,7 @@ import ( "net/mail" "testing" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/management/server/http/handlers/peers/peers_handler_test.go b/management/server/http/handlers/peers/peers_handler_test.go index 047213879..592d64d1a 100644 --- a/management/server/http/handlers/peers/peers_handler_test.go +++ b/management/server/http/handlers/peers/peers_handler_test.go @@ -13,9 +13,8 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" "github.com/gorilla/mux" - ugomock "go.uber.org/mock/gomock" + "go.uber.org/mock/gomock" "golang.org/x/exp/maps" "github.com/netbirdio/netbird/management/internals/controllers/network_map" @@ -106,7 +105,7 @@ func initTestMetaData(t *testing.T, peers ...*nbpeer.Peer) *Handler { }, } - ctrl := ugomock.NewController(t) + ctrl := gomock.NewController(t) networkMapController := network_map.NewMockController(ctrl) networkMapController.EXPECT(). diff --git a/management/server/http/handlers/policies/geolocation_handler_test.go b/management/server/http/handlers/policies/geolocation_handler_test.go index f5723b8fc..42b98734b 100644 --- a/management/server/http/handlers/policies/geolocation_handler_test.go +++ b/management/server/http/handlers/policies/geolocation_handler_test.go @@ -10,7 +10,7 @@ import ( "path/filepath" "testing" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" diff --git a/management/server/http/handlers/setup_keys/setupkeys_handler.go b/management/server/http/handlers/setup_keys/setupkeys_handler.go index d267b6eea..bb498f46b 100644 --- a/management/server/http/handlers/setup_keys/setupkeys_handler.go +++ b/management/server/http/handlers/setup_keys/setupkeys_handler.go @@ -64,6 +64,19 @@ func (h *handler) createSetupKey(w http.ResponseWriter, r *http.Request) { return } + // A one-off key can be used once, and GenerateSetupKey pins its usage limit + // at 1 whatever the request says. Silently overriding a caller that asked + // for a different number leaves them holding a key that does not do what + // they configured, and no way to find out except by using it. Only values + // above 1 are refused: usage_limit is a required field with no null, so 0 + // cannot be told apart from a caller that has nothing to say about it. + if types.SetupKeyType(req.Type) == types.SetupKeyOneOff && req.UsageLimit > 1 { + util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, + "usage_limit %d is not valid for a one-off setup key, which can be used once; use type reusable for a key that can be used more than once", + req.UsageLimit), w) + return + } + expiresIn := time.Duration(req.ExpiresIn) * time.Second if expiresIn < 0 { diff --git a/management/server/http/handlers/setup_keys/setupkeys_handler_test.go b/management/server/http/handlers/setup_keys/setupkeys_handler_test.go index b137b6dd1..a9cfd4bd3 100644 --- a/management/server/http/handlers/setup_keys/setupkeys_handler_test.go +++ b/management/server/http/handlers/setup_keys/setupkeys_handler_test.go @@ -134,6 +134,40 @@ func TestSetupKeysHandlers(t *testing.T) { expectedBody: true, expectedSetupKey: expectedNewKey, }, + { + // A one-off key is used once. Asking for more used to be accepted + // and then quietly reduced to 1. + name: "Create One-Off Setup Key With Conflicting Usage Limit", + requestType: http.MethodPost, + requestPath: "/api/setup-keys", + requestBody: bytes.NewBuffer( + []byte(fmt.Sprintf("{\"name\":\"%s\",\"type\":\"one-off\",\"expires_in\":86400,\"usage_limit\":5}", newSetupKeyName))), + expectedStatus: http.StatusUnprocessableEntity, + expectedBody: false, + }, + { + // 0 is what a caller sends when it has nothing to say about the + // usage limit, since the field is required and has no null, so it + // has to keep working. + name: "Create One-Off Setup Key Without Usage Limit", + requestType: http.MethodPost, + requestPath: "/api/setup-keys", + requestBody: bytes.NewBuffer( + []byte(fmt.Sprintf("{\"name\":\"%s\",\"type\":\"one-off\",\"expires_in\":86400,\"usage_limit\":0}", newSetupKeyName))), + expectedStatus: http.StatusOK, + expectedBody: false, + }, + { + // Only one-off keys are constrained; a reusable key means what it + // says. + name: "Create Reusable Setup Key With Usage Limit", + requestType: http.MethodPost, + requestPath: "/api/setup-keys", + requestBody: bytes.NewBuffer( + []byte(fmt.Sprintf("{\"name\":\"%s\",\"type\":\"reusable\",\"expires_in\":86400,\"usage_limit\":5}", newSetupKeyName))), + expectedStatus: http.StatusOK, + expectedBody: false, + }, { name: "Update Setup Key", requestType: http.MethodPut, diff --git a/management/server/http/testing/integration/setupkeys_handler_integration_test.go b/management/server/http/testing/integration/setupkeys_handler_integration_test.go index 0d3aaac82..21ad9d347 100644 --- a/management/server/http/testing/integration/setupkeys_handler_integration_test.go +++ b/management/server/http/testing/integration/setupkeys_handler_integration_test.go @@ -136,7 +136,10 @@ func Test_SetupKeys_Create(t *testing.T) { }, }, { - name: "Create Setup Key as on-off with more than one usage", + // The key used to be created anyway, with its usage limit quietly + // reduced to 1, so the caller was told a key they had not asked for + // was what they asked for. + name: "Create Setup Key as one-off with more than one usage", requestType: http.MethodPost, requestPath: "/api/setup-keys", requestBody: &api.CreateSetupKeyRequest{ @@ -146,23 +149,7 @@ func Test_SetupKeys_Create(t *testing.T) { Type: "one-off", UsageLimit: 3, }, - expectedStatus: http.StatusOK, - expectedResponse: &api.SetupKey{ - AutoGroups: []string{}, - Ephemeral: false, - Expires: time.Time{}, - Id: "", - Key: "", - LastUsed: time.Time{}, - Name: testing_tools.NewKeyName, - Revoked: false, - State: "valid", - Type: "one-off", - UpdatedAt: time.Now(), - UsageLimit: 1, - UsedTimes: 0, - Valid: true, - }, + expectedStatus: http.StatusUnprocessableEntity, }, { name: "Create Setup Key with expiration in the past", diff --git a/management/server/identity_provider_test.go b/management/server/identity_provider_test.go index d51254c55..b55d4f24c 100644 --- a/management/server/identity_provider_test.go +++ b/management/server/identity_provider_test.go @@ -10,7 +10,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/management/server/instance/setup_service_test.go b/management/server/instance/setup_service_test.go index 12ec7d0fa..af3a91b75 100644 --- a/management/server/instance/setup_service_test.go +++ b/management/server/instance/setup_service_test.go @@ -5,7 +5,7 @@ import ( "errors" "testing" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/management/server/management_proto_test.go b/management/server/management_proto_test.go index 45d4ab8c9..c23ca6237 100644 --- a/management/server/management_proto_test.go +++ b/management/server/management_proto_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" diff --git a/management/server/management_test.go b/management/server/management_test.go index f1d49193c..80c76f0de 100644 --- a/management/server/management_test.go +++ b/management/server/management_test.go @@ -10,7 +10,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" pb "github.com/golang/protobuf/proto" //nolint log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" diff --git a/management/server/nameserver_test.go b/management/server/nameserver_test.go index e13b0bb19..ce5d5d57b 100644 --- a/management/server/nameserver_test.go +++ b/management/server/nameserver_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/management/server/networks/resources/manager_test.go b/management/server/networks/resources/manager_test.go index c6d8e7bcc..bd9dd84dd 100644 --- a/management/server/networks/resources/manager_test.go +++ b/management/server/networks/resources/manager_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/stretchr/testify/require" reverseproxy "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/service" diff --git a/management/server/peer_test.go b/management/server/peer_test.go index a7f8ba695..80d270e98 100644 --- a/management/server/peer_test.go +++ b/management/server/peer_test.go @@ -16,7 +16,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/rs/xid" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" diff --git a/management/server/permissions/manager.go b/management/server/permissions/manager.go index 6b9977a86..90166acbd 100644 --- a/management/server/permissions/manager.go +++ b/management/server/permissions/manager.go @@ -1,6 +1,6 @@ package permissions -//go:generate go run github.com/golang/mock/mockgen -package permissions -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod +//go:generate go tool mockgen -package permissions -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod import ( "context" diff --git a/management/server/permissions/manager_mock.go b/management/server/permissions/manager_mock.go index 934e33398..251e456d4 100644 --- a/management/server/permissions/manager_mock.go +++ b/management/server/permissions/manager_mock.go @@ -1,5 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: ./manager.go +// +// Generated by this command: +// +// mockgen -package permissions -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod +// // Package permissions is a generated GoMock package. package permissions @@ -8,18 +13,19 @@ import ( context "context" reflect "reflect" - gomock "github.com/golang/mock/gomock" account "github.com/netbirdio/netbird/management/server/account" modules "github.com/netbirdio/netbird/management/server/permissions/modules" operations "github.com/netbirdio/netbird/management/server/permissions/operations" roles "github.com/netbirdio/netbird/management/server/permissions/roles" types "github.com/netbirdio/netbird/management/server/types" + gomock "go.uber.org/mock/gomock" ) // MockManager is a mock of Manager interface. type MockManager struct { ctrl *gomock.Controller recorder *MockManagerMockRecorder + isgomock struct{} } // MockManagerMockRecorder is the mock recorder for MockManager. @@ -49,7 +55,7 @@ func (m *MockManager) GetPermissionsByRole(ctx context.Context, role types.UserR } // GetPermissionsByRole indicates an expected call of GetPermissionsByRole. -func (mr *MockManagerMockRecorder) GetPermissionsByRole(ctx, role interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetPermissionsByRole(ctx, role any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPermissionsByRole", reflect.TypeOf((*MockManager)(nil).GetPermissionsByRole), ctx, role) } @@ -61,7 +67,7 @@ func (m *MockManager) SetAccountManager(accountManager account.Manager) { } // SetAccountManager indicates an expected call of SetAccountManager. -func (mr *MockManagerMockRecorder) SetAccountManager(accountManager interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) SetAccountManager(accountManager any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetAccountManager", reflect.TypeOf((*MockManager)(nil).SetAccountManager), accountManager) } @@ -76,7 +82,7 @@ func (m *MockManager) ValidateAccountAccess(ctx context.Context, accountID strin } // ValidateAccountAccess indicates an expected call of ValidateAccountAccess. -func (mr *MockManagerMockRecorder) ValidateAccountAccess(ctx, accountID, user, allowOwnerAndAdmin interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) ValidateAccountAccess(ctx, accountID, user, allowOwnerAndAdmin any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateAccountAccess", reflect.TypeOf((*MockManager)(nil).ValidateAccountAccess), ctx, accountID, user, allowOwnerAndAdmin) } @@ -90,7 +96,7 @@ func (m *MockManager) ValidateRoleModuleAccess(ctx context.Context, accountID st } // ValidateRoleModuleAccess indicates an expected call of ValidateRoleModuleAccess. -func (mr *MockManagerMockRecorder) ValidateRoleModuleAccess(ctx, accountID, role, module, operation interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) ValidateRoleModuleAccess(ctx, accountID, role, module, operation any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateRoleModuleAccess", reflect.TypeOf((*MockManager)(nil).ValidateRoleModuleAccess), ctx, accountID, role, module, operation) } @@ -106,7 +112,7 @@ func (m *MockManager) ValidateUserPermissions(ctx context.Context, accountID, us } // ValidateUserPermissions indicates an expected call of ValidateUserPermissions. -func (mr *MockManagerMockRecorder) ValidateUserPermissions(ctx, accountID, userID, module, operation interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) ValidateUserPermissions(ctx, accountID, userID, module, operation any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateUserPermissions", reflect.TypeOf((*MockManager)(nil).ValidateUserPermissions), ctx, accountID, userID, module, operation) } diff --git a/management/server/route_test.go b/management/server/route_test.go index 5ae18c253..53dbb29d9 100644 --- a/management/server/route_test.go +++ b/management/server/route_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "github.com/rs/xid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/management/server/settings/manager.go b/management/server/settings/manager.go index f84739193..dc5b46471 100644 --- a/management/server/settings/manager.go +++ b/management/server/settings/manager.go @@ -1,6 +1,6 @@ package settings -//go:generate go run github.com/golang/mock/mockgen -package settings -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod +//go:generate go tool mockgen -package settings -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod import ( "context" diff --git a/management/server/settings/manager_mock.go b/management/server/settings/manager_mock.go index 4bedb2cf7..59b321875 100644 --- a/management/server/settings/manager_mock.go +++ b/management/server/settings/manager_mock.go @@ -1,5 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: ./manager.go +// +// Generated by this command: +// +// mockgen -package settings -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod +// // Package settings is a generated GoMock package. package settings @@ -9,15 +14,16 @@ import ( netip "net/netip" reflect "reflect" - gomock "github.com/golang/mock/gomock" extra_settings "github.com/netbirdio/netbird/management/server/integrations/extra_settings" types "github.com/netbirdio/netbird/management/server/types" + gomock "go.uber.org/mock/gomock" ) // MockManager is a mock of Manager interface. type MockManager struct { ctrl *gomock.Controller recorder *MockManagerMockRecorder + isgomock struct{} } // MockManagerMockRecorder is the mock recorder for MockManager. @@ -37,6 +43,22 @@ func (m *MockManager) EXPECT() *MockManagerMockRecorder { return m.recorder } +// GetEffectiveNetworkRanges mocks base method. +func (m *MockManager) GetEffectiveNetworkRanges(ctx context.Context, accountID string) (netip.Prefix, netip.Prefix, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetEffectiveNetworkRanges", ctx, accountID) + ret0, _ := ret[0].(netip.Prefix) + ret1, _ := ret[1].(netip.Prefix) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetEffectiveNetworkRanges indicates an expected call of GetEffectiveNetworkRanges. +func (mr *MockManagerMockRecorder) GetEffectiveNetworkRanges(ctx, accountID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEffectiveNetworkRanges", reflect.TypeOf((*MockManager)(nil).GetEffectiveNetworkRanges), ctx, accountID) +} + // GetExtraSettings mocks base method. func (m *MockManager) GetExtraSettings(ctx context.Context, accountID string) (*types.ExtraSettings, error) { m.ctrl.T.Helper() @@ -47,7 +69,7 @@ func (m *MockManager) GetExtraSettings(ctx context.Context, accountID string) (* } // GetExtraSettings indicates an expected call of GetExtraSettings. -func (mr *MockManagerMockRecorder) GetExtraSettings(ctx, accountID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetExtraSettings(ctx, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExtraSettings", reflect.TypeOf((*MockManager)(nil).GetExtraSettings), ctx, accountID) } @@ -76,7 +98,7 @@ func (m *MockManager) GetSettings(ctx context.Context, accountID, userID string) } // GetSettings indicates an expected call of GetSettings. -func (mr *MockManagerMockRecorder) GetSettings(ctx, accountID, userID interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) GetSettings(ctx, accountID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSettings", reflect.TypeOf((*MockManager)(nil).GetSettings), ctx, accountID, userID) } @@ -91,23 +113,7 @@ func (m *MockManager) UpdateExtraSettings(ctx context.Context, accountID, userID } // UpdateExtraSettings indicates an expected call of UpdateExtraSettings. -func (mr *MockManagerMockRecorder) UpdateExtraSettings(ctx, accountID, userID, extraSettings interface{}) *gomock.Call { +func (mr *MockManagerMockRecorder) UpdateExtraSettings(ctx, accountID, userID, extraSettings any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateExtraSettings", reflect.TypeOf((*MockManager)(nil).UpdateExtraSettings), ctx, accountID, userID, extraSettings) } - -// GetEffectiveNetworkRanges mocks base method. -func (m *MockManager) GetEffectiveNetworkRanges(ctx context.Context, accountID string) (netip.Prefix, netip.Prefix, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetEffectiveNetworkRanges", ctx, accountID) - ret0, _ := ret[0].(netip.Prefix) - ret1, _ := ret[1].(netip.Prefix) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// GetEffectiveNetworkRanges indicates an expected call of GetEffectiveNetworkRanges. -func (mr *MockManagerMockRecorder) GetEffectiveNetworkRanges(ctx, accountID interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEffectiveNetworkRanges", reflect.TypeOf((*MockManager)(nil).GetEffectiveNetworkRanges), ctx, accountID) -} diff --git a/management/server/store/store.go b/management/server/store/store.go index 869e8dab5..ca911092b 100644 --- a/management/server/store/store.go +++ b/management/server/store/store.go @@ -1,6 +1,6 @@ package store -//go:generate go run github.com/golang/mock/mockgen -package store -destination=store_mock.go -source=./store.go -build_flags=-mod=mod +//go:generate go tool mockgen -package store -destination=store_mock.go -source=./store.go -build_flags=-mod=mod import ( "context" diff --git a/management/server/store/store_mock.go b/management/server/store/store_mock.go index 3d9e160ba..70acb9f58 100644 --- a/management/server/store/store_mock.go +++ b/management/server/store/store_mock.go @@ -1,5 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: ./store.go +// +// Generated by this command: +// +// mockgen -package store -destination=store_mock.go -source=./store.go -build_flags=-mod=mod +// // Package store is a generated GoMock package. package store @@ -11,7 +16,6 @@ import ( reflect "reflect" time "time" - gomock "github.com/golang/mock/gomock" dns "github.com/netbirdio/netbird/dns" types "github.com/netbirdio/netbird/management/internals/modules/agentnetwork/types" accesslogs "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/accesslogs" @@ -28,12 +32,14 @@ import ( types3 "github.com/netbirdio/netbird/management/server/types" route "github.com/netbirdio/netbird/route" crypt "github.com/netbirdio/netbird/util/crypt" + gomock "go.uber.org/mock/gomock" ) // MockStore is a mock of Store interface. type MockStore struct { ctrl *gomock.Controller recorder *MockStoreMockRecorder + isgomock struct{} } // MockStoreMockRecorder is the mock recorder for MockStore. @@ -63,7 +69,7 @@ func (m *MockStore) AccountExists(ctx context.Context, lockStrength LockingStren } // AccountExists indicates an expected call of AccountExists. -func (mr *MockStoreMockRecorder) AccountExists(ctx, lockStrength, id interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) AccountExists(ctx, lockStrength, id any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AccountExists", reflect.TypeOf((*MockStore)(nil).AccountExists), ctx, lockStrength, id) } @@ -77,23 +83,23 @@ func (m *MockStore) AcquireGlobalLock(ctx context.Context) func() { } // AcquireGlobalLock indicates an expected call of AcquireGlobalLock. -func (mr *MockStoreMockRecorder) AcquireGlobalLock(ctx interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) AcquireGlobalLock(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcquireGlobalLock", reflect.TypeOf((*MockStore)(nil).AcquireGlobalLock), ctx) } // AddPeerToAccount mocks base method. -func (m *MockStore) AddPeerToAccount(ctx context.Context, peer *peer.Peer) error { +func (m *MockStore) AddPeerToAccount(ctx context.Context, arg1 *peer.Peer) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AddPeerToAccount", ctx, peer) + ret := m.ctrl.Call(m, "AddPeerToAccount", ctx, arg1) ret0, _ := ret[0].(error) return ret0 } // AddPeerToAccount indicates an expected call of AddPeerToAccount. -func (mr *MockStoreMockRecorder) AddPeerToAccount(ctx, peer interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) AddPeerToAccount(ctx, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddPeerToAccount", reflect.TypeOf((*MockStore)(nil).AddPeerToAccount), ctx, peer) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddPeerToAccount", reflect.TypeOf((*MockStore)(nil).AddPeerToAccount), ctx, arg1) } // AddPeerToAllGroup mocks base method. @@ -105,7 +111,7 @@ func (m *MockStore) AddPeerToAllGroup(ctx context.Context, accountID, peerID str } // AddPeerToAllGroup indicates an expected call of AddPeerToAllGroup. -func (mr *MockStoreMockRecorder) AddPeerToAllGroup(ctx, accountID, peerID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) AddPeerToAllGroup(ctx, accountID, peerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddPeerToAllGroup", reflect.TypeOf((*MockStore)(nil).AddPeerToAllGroup), ctx, accountID, peerID) } @@ -119,7 +125,7 @@ func (m *MockStore) AddPeerToGroup(ctx context.Context, accountID, peerId, group } // AddPeerToGroup indicates an expected call of AddPeerToGroup. -func (mr *MockStoreMockRecorder) AddPeerToGroup(ctx, accountID, peerId, groupID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) AddPeerToGroup(ctx, accountID, peerId, groupID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddPeerToGroup", reflect.TypeOf((*MockStore)(nil).AddPeerToGroup), ctx, accountID, peerId, groupID) } @@ -133,7 +139,7 @@ func (m *MockStore) AddResourceToGroup(ctx context.Context, accountId, groupID s } // AddResourceToGroup indicates an expected call of AddResourceToGroup. -func (mr *MockStoreMockRecorder) AddResourceToGroup(ctx, accountId, groupID, resource interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) AddResourceToGroup(ctx, accountId, groupID, resource any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddResourceToGroup", reflect.TypeOf((*MockStore)(nil).AddResourceToGroup), ctx, accountId, groupID, resource) } @@ -148,7 +154,7 @@ func (m *MockStore) ApproveAccountPeers(ctx context.Context, accountID string) ( } // ApproveAccountPeers indicates an expected call of ApproveAccountPeers. -func (mr *MockStoreMockRecorder) ApproveAccountPeers(ctx, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) ApproveAccountPeers(ctx, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApproveAccountPeers", reflect.TypeOf((*MockStore)(nil).ApproveAccountPeers), ctx, accountID) } @@ -162,7 +168,7 @@ func (m *MockStore) CleanupStaleProxies(ctx context.Context, inactivityDuration } // CleanupStaleProxies indicates an expected call of CleanupStaleProxies. -func (mr *MockStoreMockRecorder) CleanupStaleProxies(ctx, inactivityDuration interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) CleanupStaleProxies(ctx, inactivityDuration any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CleanupStaleProxies", reflect.TypeOf((*MockStore)(nil).CleanupStaleProxies), ctx, inactivityDuration) } @@ -176,7 +182,7 @@ func (m *MockStore) Close(ctx context.Context) error { } // Close indicates an expected call of Close. -func (mr *MockStoreMockRecorder) Close(ctx interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) Close(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockStore)(nil).Close), ctx) } @@ -190,24 +196,24 @@ func (m *MockStore) CompletePeerJob(ctx context.Context, job *types3.Job) error } // CompletePeerJob indicates an expected call of CompletePeerJob. -func (mr *MockStoreMockRecorder) CompletePeerJob(ctx, job interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) CompletePeerJob(ctx, job any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CompletePeerJob", reflect.TypeOf((*MockStore)(nil).CompletePeerJob), ctx, job) } // CountAccountsByPrivateDomain mocks base method. -func (m *MockStore) CountAccountsByPrivateDomain(ctx context.Context, domain string) (int64, error) { +func (m *MockStore) CountAccountsByPrivateDomain(ctx context.Context, arg1 string) (int64, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CountAccountsByPrivateDomain", ctx, domain) + ret := m.ctrl.Call(m, "CountAccountsByPrivateDomain", ctx, arg1) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // CountAccountsByPrivateDomain indicates an expected call of CountAccountsByPrivateDomain. -func (mr *MockStoreMockRecorder) CountAccountsByPrivateDomain(ctx, domain interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) CountAccountsByPrivateDomain(ctx, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountAccountsByPrivateDomain", reflect.TypeOf((*MockStore)(nil).CountAccountsByPrivateDomain), ctx, domain) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountAccountsByPrivateDomain", reflect.TypeOf((*MockStore)(nil).CountAccountsByPrivateDomain), ctx, arg1) } // CountEphemeralServicesByPeer mocks base method. @@ -220,7 +226,7 @@ func (m *MockStore) CountEphemeralServicesByPeer(ctx context.Context, lockStreng } // CountEphemeralServicesByPeer indicates an expected call of CountEphemeralServicesByPeer. -func (mr *MockStoreMockRecorder) CountEphemeralServicesByPeer(ctx, lockStrength, accountID, peerID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) CountEphemeralServicesByPeer(ctx, lockStrength, accountID, peerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountEphemeralServicesByPeer", reflect.TypeOf((*MockStore)(nil).CountEphemeralServicesByPeer), ctx, lockStrength, accountID, peerID) } @@ -235,7 +241,7 @@ func (m *MockStore) CountProxiesByAccountID(ctx context.Context, accountID strin } // CountProxiesByAccountID indicates an expected call of CountProxiesByAccountID. -func (mr *MockStoreMockRecorder) CountProxiesByAccountID(ctx, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) CountProxiesByAccountID(ctx, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountProxiesByAccountID", reflect.TypeOf((*MockStore)(nil).CountProxiesByAccountID), ctx, accountID) } @@ -249,7 +255,7 @@ func (m *MockStore) CreateAccessLog(ctx context.Context, log *accesslogs.AccessL } // CreateAccessLog indicates an expected call of CreateAccessLog. -func (mr *MockStoreMockRecorder) CreateAccessLog(ctx, log interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) CreateAccessLog(ctx, log any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAccessLog", reflect.TypeOf((*MockStore)(nil).CreateAccessLog), ctx, log) } @@ -263,7 +269,7 @@ func (m *MockStore) CreateAgentNetworkAccessLog(ctx context.Context, entry *type } // CreateAgentNetworkAccessLog indicates an expected call of CreateAgentNetworkAccessLog. -func (mr *MockStoreMockRecorder) CreateAgentNetworkAccessLog(ctx, entry, groups interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) CreateAgentNetworkAccessLog(ctx, entry, groups any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAgentNetworkAccessLog", reflect.TypeOf((*MockStore)(nil).CreateAgentNetworkAccessLog), ctx, entry, groups) } @@ -277,7 +283,7 @@ func (m *MockStore) CreateAgentNetworkSettings(ctx context.Context, settings *ty } // CreateAgentNetworkSettings indicates an expected call of CreateAgentNetworkSettings. -func (mr *MockStoreMockRecorder) CreateAgentNetworkSettings(ctx, settings interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) CreateAgentNetworkSettings(ctx, settings any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAgentNetworkSettings", reflect.TypeOf((*MockStore)(nil).CreateAgentNetworkSettings), ctx, settings) } @@ -291,7 +297,7 @@ func (m *MockStore) CreateAgentNetworkUsage(ctx context.Context, usage *types.Ag } // CreateAgentNetworkUsage indicates an expected call of CreateAgentNetworkUsage. -func (mr *MockStoreMockRecorder) CreateAgentNetworkUsage(ctx, usage, groups interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) CreateAgentNetworkUsage(ctx, usage, groups any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAgentNetworkUsage", reflect.TypeOf((*MockStore)(nil).CreateAgentNetworkUsage), ctx, usage, groups) } @@ -306,7 +312,7 @@ func (m *MockStore) CreateCustomDomain(ctx context.Context, accountID, domainNam } // CreateCustomDomain indicates an expected call of CreateCustomDomain. -func (mr *MockStoreMockRecorder) CreateCustomDomain(ctx, accountID, domainName, targetCluster, validated interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) CreateCustomDomain(ctx, accountID, domainName, targetCluster, validated any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCustomDomain", reflect.TypeOf((*MockStore)(nil).CreateCustomDomain), ctx, accountID, domainName, targetCluster, validated) } @@ -320,7 +326,7 @@ func (m *MockStore) CreateDNSRecord(ctx context.Context, record *records.Record) } // CreateDNSRecord indicates an expected call of CreateDNSRecord. -func (mr *MockStoreMockRecorder) CreateDNSRecord(ctx, record interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) CreateDNSRecord(ctx, record any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDNSRecord", reflect.TypeOf((*MockStore)(nil).CreateDNSRecord), ctx, record) } @@ -334,7 +340,7 @@ func (m *MockStore) CreateGroup(ctx context.Context, group *types3.Group) error } // CreateGroup indicates an expected call of CreateGroup. -func (mr *MockStoreMockRecorder) CreateGroup(ctx, group interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) CreateGroup(ctx, group any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateGroup", reflect.TypeOf((*MockStore)(nil).CreateGroup), ctx, group) } @@ -348,7 +354,7 @@ func (m *MockStore) CreateGroups(ctx context.Context, accountID string, groups [ } // CreateGroups indicates an expected call of CreateGroups. -func (mr *MockStoreMockRecorder) CreateGroups(ctx, accountID, groups interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) CreateGroups(ctx, accountID, groups any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateGroups", reflect.TypeOf((*MockStore)(nil).CreateGroups), ctx, accountID, groups) } @@ -362,7 +368,7 @@ func (m *MockStore) CreateNetworkRouter(ctx context.Context, router *types1.Netw } // CreateNetworkRouter indicates an expected call of CreateNetworkRouter. -func (mr *MockStoreMockRecorder) CreateNetworkRouter(ctx, router interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) CreateNetworkRouter(ctx, router any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNetworkRouter", reflect.TypeOf((*MockStore)(nil).CreateNetworkRouter), ctx, router) } @@ -376,7 +382,7 @@ func (m *MockStore) CreatePeerJob(ctx context.Context, job *types3.Job) error { } // CreatePeerJob indicates an expected call of CreatePeerJob. -func (mr *MockStoreMockRecorder) CreatePeerJob(ctx, job interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) CreatePeerJob(ctx, job any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatePeerJob", reflect.TypeOf((*MockStore)(nil).CreatePeerJob), ctx, job) } @@ -390,23 +396,23 @@ func (m *MockStore) CreatePolicy(ctx context.Context, policy *types3.Policy) err } // CreatePolicy indicates an expected call of CreatePolicy. -func (mr *MockStoreMockRecorder) CreatePolicy(ctx, policy interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) CreatePolicy(ctx, policy any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatePolicy", reflect.TypeOf((*MockStore)(nil).CreatePolicy), ctx, policy) } // CreateService mocks base method. -func (m *MockStore) CreateService(ctx context.Context, service *service.Service) error { +func (m *MockStore) CreateService(ctx context.Context, arg1 *service.Service) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateService", ctx, service) + ret := m.ctrl.Call(m, "CreateService", ctx, arg1) ret0, _ := ret[0].(error) return ret0 } // CreateService indicates an expected call of CreateService. -func (mr *MockStoreMockRecorder) CreateService(ctx, service interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) CreateService(ctx, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateService", reflect.TypeOf((*MockStore)(nil).CreateService), ctx, service) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateService", reflect.TypeOf((*MockStore)(nil).CreateService), ctx, arg1) } // CreateZone mocks base method. @@ -418,7 +424,7 @@ func (m *MockStore) CreateZone(ctx context.Context, zone *zones.Zone) error { } // CreateZone indicates an expected call of CreateZone. -func (mr *MockStoreMockRecorder) CreateZone(ctx, zone interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) CreateZone(ctx, zone any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateZone", reflect.TypeOf((*MockStore)(nil).CreateZone), ctx, zone) } @@ -432,7 +438,7 @@ func (m *MockStore) DeleteAccount(ctx context.Context, account *types3.Account) } // DeleteAccount indicates an expected call of DeleteAccount. -func (mr *MockStoreMockRecorder) DeleteAccount(ctx, account interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteAccount(ctx, account any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAccount", reflect.TypeOf((*MockStore)(nil).DeleteAccount), ctx, account) } @@ -446,7 +452,7 @@ func (m *MockStore) DeleteAccountCluster(ctx context.Context, clusterAddress, ac } // DeleteAccountCluster indicates an expected call of DeleteAccountCluster. -func (mr *MockStoreMockRecorder) DeleteAccountCluster(ctx, clusterAddress, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteAccountCluster(ctx, clusterAddress, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAccountCluster", reflect.TypeOf((*MockStore)(nil).DeleteAccountCluster), ctx, clusterAddress, accountID) } @@ -460,7 +466,7 @@ func (m *MockStore) DeleteAgentNetworkBudgetRule(ctx context.Context, accountID, } // DeleteAgentNetworkBudgetRule indicates an expected call of DeleteAgentNetworkBudgetRule. -func (mr *MockStoreMockRecorder) DeleteAgentNetworkBudgetRule(ctx, accountID, ruleID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteAgentNetworkBudgetRule(ctx, accountID, ruleID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAgentNetworkBudgetRule", reflect.TypeOf((*MockStore)(nil).DeleteAgentNetworkBudgetRule), ctx, accountID, ruleID) } @@ -474,7 +480,7 @@ func (m *MockStore) DeleteAgentNetworkGuardrail(ctx context.Context, accountID, } // DeleteAgentNetworkGuardrail indicates an expected call of DeleteAgentNetworkGuardrail. -func (mr *MockStoreMockRecorder) DeleteAgentNetworkGuardrail(ctx, accountID, guardrailID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteAgentNetworkGuardrail(ctx, accountID, guardrailID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAgentNetworkGuardrail", reflect.TypeOf((*MockStore)(nil).DeleteAgentNetworkGuardrail), ctx, accountID, guardrailID) } @@ -488,7 +494,7 @@ func (m *MockStore) DeleteAgentNetworkPolicy(ctx context.Context, accountID, pol } // DeleteAgentNetworkPolicy indicates an expected call of DeleteAgentNetworkPolicy. -func (mr *MockStoreMockRecorder) DeleteAgentNetworkPolicy(ctx, accountID, policyID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteAgentNetworkPolicy(ctx, accountID, policyID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAgentNetworkPolicy", reflect.TypeOf((*MockStore)(nil).DeleteAgentNetworkPolicy), ctx, accountID, policyID) } @@ -502,7 +508,7 @@ func (m *MockStore) DeleteAgentNetworkProvider(ctx context.Context, accountID, p } // DeleteAgentNetworkProvider indicates an expected call of DeleteAgentNetworkProvider. -func (mr *MockStoreMockRecorder) DeleteAgentNetworkProvider(ctx, accountID, providerID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteAgentNetworkProvider(ctx, accountID, providerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAgentNetworkProvider", reflect.TypeOf((*MockStore)(nil).DeleteAgentNetworkProvider), ctx, accountID, providerID) } @@ -516,7 +522,7 @@ func (m *MockStore) DeleteAgentNetworkSettings(ctx context.Context, accountID st } // DeleteAgentNetworkSettings indicates an expected call of DeleteAgentNetworkSettings. -func (mr *MockStoreMockRecorder) DeleteAgentNetworkSettings(ctx, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteAgentNetworkSettings(ctx, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAgentNetworkSettings", reflect.TypeOf((*MockStore)(nil).DeleteAgentNetworkSettings), ctx, accountID) } @@ -530,7 +536,7 @@ func (m *MockStore) DeleteCustomDomain(ctx context.Context, accountID, domainID } // DeleteCustomDomain indicates an expected call of DeleteCustomDomain. -func (mr *MockStoreMockRecorder) DeleteCustomDomain(ctx, accountID, domainID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteCustomDomain(ctx, accountID, domainID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCustomDomain", reflect.TypeOf((*MockStore)(nil).DeleteCustomDomain), ctx, accountID, domainID) } @@ -544,7 +550,7 @@ func (m *MockStore) DeleteDNSRecord(ctx context.Context, accountID, zoneID, reco } // DeleteDNSRecord indicates an expected call of DeleteDNSRecord. -func (mr *MockStoreMockRecorder) DeleteDNSRecord(ctx, accountID, zoneID, recordID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteDNSRecord(ctx, accountID, zoneID, recordID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDNSRecord", reflect.TypeOf((*MockStore)(nil).DeleteDNSRecord), ctx, accountID, zoneID, recordID) } @@ -558,7 +564,7 @@ func (m *MockStore) DeleteGroup(ctx context.Context, accountID, groupID string) } // DeleteGroup indicates an expected call of DeleteGroup. -func (mr *MockStoreMockRecorder) DeleteGroup(ctx, accountID, groupID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteGroup(ctx, accountID, groupID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteGroup", reflect.TypeOf((*MockStore)(nil).DeleteGroup), ctx, accountID, groupID) } @@ -572,7 +578,7 @@ func (m *MockStore) DeleteGroups(ctx context.Context, accountID string, groupIDs } // DeleteGroups indicates an expected call of DeleteGroups. -func (mr *MockStoreMockRecorder) DeleteGroups(ctx, accountID, groupIDs interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteGroups(ctx, accountID, groupIDs any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteGroups", reflect.TypeOf((*MockStore)(nil).DeleteGroups), ctx, accountID, groupIDs) } @@ -586,7 +592,7 @@ func (m *MockStore) DeleteHashedPAT2TokenIDIndex(hashedToken string) error { } // DeleteHashedPAT2TokenIDIndex indicates an expected call of DeleteHashedPAT2TokenIDIndex. -func (mr *MockStoreMockRecorder) DeleteHashedPAT2TokenIDIndex(hashedToken interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteHashedPAT2TokenIDIndex(hashedToken any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteHashedPAT2TokenIDIndex", reflect.TypeOf((*MockStore)(nil).DeleteHashedPAT2TokenIDIndex), hashedToken) } @@ -600,7 +606,7 @@ func (m *MockStore) DeleteNameServerGroup(ctx context.Context, accountID, nameSe } // DeleteNameServerGroup indicates an expected call of DeleteNameServerGroup. -func (mr *MockStoreMockRecorder) DeleteNameServerGroup(ctx, accountID, nameServerGroupID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteNameServerGroup(ctx, accountID, nameServerGroupID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteNameServerGroup", reflect.TypeOf((*MockStore)(nil).DeleteNameServerGroup), ctx, accountID, nameServerGroupID) } @@ -614,7 +620,7 @@ func (m *MockStore) DeleteNetwork(ctx context.Context, accountID, networkID stri } // DeleteNetwork indicates an expected call of DeleteNetwork. -func (mr *MockStoreMockRecorder) DeleteNetwork(ctx, accountID, networkID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteNetwork(ctx, accountID, networkID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteNetwork", reflect.TypeOf((*MockStore)(nil).DeleteNetwork), ctx, accountID, networkID) } @@ -628,7 +634,7 @@ func (m *MockStore) DeleteNetworkResource(ctx context.Context, accountID, resour } // DeleteNetworkResource indicates an expected call of DeleteNetworkResource. -func (mr *MockStoreMockRecorder) DeleteNetworkResource(ctx, accountID, resourceID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteNetworkResource(ctx, accountID, resourceID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteNetworkResource", reflect.TypeOf((*MockStore)(nil).DeleteNetworkResource), ctx, accountID, resourceID) } @@ -642,7 +648,7 @@ func (m *MockStore) DeleteNetworkRouter(ctx context.Context, accountID, routerID } // DeleteNetworkRouter indicates an expected call of DeleteNetworkRouter. -func (mr *MockStoreMockRecorder) DeleteNetworkRouter(ctx, accountID, routerID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteNetworkRouter(ctx, accountID, routerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteNetworkRouter", reflect.TypeOf((*MockStore)(nil).DeleteNetworkRouter), ctx, accountID, routerID) } @@ -657,7 +663,7 @@ func (m *MockStore) DeleteOldAccessLogs(ctx context.Context, olderThan time.Time } // DeleteOldAccessLogs indicates an expected call of DeleteOldAccessLogs. -func (mr *MockStoreMockRecorder) DeleteOldAccessLogs(ctx, olderThan interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteOldAccessLogs(ctx, olderThan any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOldAccessLogs", reflect.TypeOf((*MockStore)(nil).DeleteOldAccessLogs), ctx, olderThan) } @@ -672,7 +678,7 @@ func (m *MockStore) DeleteOldAgentNetworkAccessLogs(ctx context.Context, account } // DeleteOldAgentNetworkAccessLogs indicates an expected call of DeleteOldAgentNetworkAccessLogs. -func (mr *MockStoreMockRecorder) DeleteOldAgentNetworkAccessLogs(ctx, accountID, olderThan interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteOldAgentNetworkAccessLogs(ctx, accountID, olderThan any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOldAgentNetworkAccessLogs", reflect.TypeOf((*MockStore)(nil).DeleteOldAgentNetworkAccessLogs), ctx, accountID, olderThan) } @@ -686,7 +692,7 @@ func (m *MockStore) DeletePAT(ctx context.Context, userID, patID string) error { } // DeletePAT indicates an expected call of DeletePAT. -func (mr *MockStoreMockRecorder) DeletePAT(ctx, userID, patID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeletePAT(ctx, userID, patID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePAT", reflect.TypeOf((*MockStore)(nil).DeletePAT), ctx, userID, patID) } @@ -700,7 +706,7 @@ func (m *MockStore) DeletePeer(ctx context.Context, accountID, peerID string) er } // DeletePeer indicates an expected call of DeletePeer. -func (mr *MockStoreMockRecorder) DeletePeer(ctx, accountID, peerID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeletePeer(ctx, accountID, peerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePeer", reflect.TypeOf((*MockStore)(nil).DeletePeer), ctx, accountID, peerID) } @@ -714,7 +720,7 @@ func (m *MockStore) DeletePolicy(ctx context.Context, accountID, policyID string } // DeletePolicy indicates an expected call of DeletePolicy. -func (mr *MockStoreMockRecorder) DeletePolicy(ctx, accountID, policyID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeletePolicy(ctx, accountID, policyID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePolicy", reflect.TypeOf((*MockStore)(nil).DeletePolicy), ctx, accountID, policyID) } @@ -728,7 +734,7 @@ func (m *MockStore) DeletePostureChecks(ctx context.Context, accountID, postureC } // DeletePostureChecks indicates an expected call of DeletePostureChecks. -func (mr *MockStoreMockRecorder) DeletePostureChecks(ctx, accountID, postureChecksID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeletePostureChecks(ctx, accountID, postureChecksID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePostureChecks", reflect.TypeOf((*MockStore)(nil).DeletePostureChecks), ctx, accountID, postureChecksID) } @@ -742,7 +748,7 @@ func (m *MockStore) DeleteRoute(ctx context.Context, accountID, routeID string) } // DeleteRoute indicates an expected call of DeleteRoute. -func (mr *MockStoreMockRecorder) DeleteRoute(ctx, accountID, routeID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteRoute(ctx, accountID, routeID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteRoute", reflect.TypeOf((*MockStore)(nil).DeleteRoute), ctx, accountID, routeID) } @@ -756,7 +762,7 @@ func (m *MockStore) DeleteService(ctx context.Context, accountID, serviceID stri } // DeleteService indicates an expected call of DeleteService. -func (mr *MockStoreMockRecorder) DeleteService(ctx, accountID, serviceID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteService(ctx, accountID, serviceID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteService", reflect.TypeOf((*MockStore)(nil).DeleteService), ctx, accountID, serviceID) } @@ -770,7 +776,7 @@ func (m *MockStore) DeleteServiceTargets(ctx context.Context, accountID, service } // DeleteServiceTargets indicates an expected call of DeleteServiceTargets. -func (mr *MockStoreMockRecorder) DeleteServiceTargets(ctx, accountID, serviceID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteServiceTargets(ctx, accountID, serviceID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteServiceTargets", reflect.TypeOf((*MockStore)(nil).DeleteServiceTargets), ctx, accountID, serviceID) } @@ -784,7 +790,7 @@ func (m *MockStore) DeleteSetupKey(ctx context.Context, accountID, keyID string) } // DeleteSetupKey indicates an expected call of DeleteSetupKey. -func (mr *MockStoreMockRecorder) DeleteSetupKey(ctx, accountID, keyID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteSetupKey(ctx, accountID, keyID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteSetupKey", reflect.TypeOf((*MockStore)(nil).DeleteSetupKey), ctx, accountID, keyID) } @@ -798,7 +804,7 @@ func (m *MockStore) DeleteTarget(ctx context.Context, accountID, serviceID strin } // DeleteTarget indicates an expected call of DeleteTarget. -func (mr *MockStoreMockRecorder) DeleteTarget(ctx, accountID, serviceID, targetID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteTarget(ctx, accountID, serviceID, targetID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTarget", reflect.TypeOf((*MockStore)(nil).DeleteTarget), ctx, accountID, serviceID, targetID) } @@ -812,7 +818,7 @@ func (m *MockStore) DeleteTokenID2UserIDIndex(tokenID string) error { } // DeleteTokenID2UserIDIndex indicates an expected call of DeleteTokenID2UserIDIndex. -func (mr *MockStoreMockRecorder) DeleteTokenID2UserIDIndex(tokenID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteTokenID2UserIDIndex(tokenID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTokenID2UserIDIndex", reflect.TypeOf((*MockStore)(nil).DeleteTokenID2UserIDIndex), tokenID) } @@ -826,7 +832,7 @@ func (m *MockStore) DeleteUser(ctx context.Context, accountID, userID string) er } // DeleteUser indicates an expected call of DeleteUser. -func (mr *MockStoreMockRecorder) DeleteUser(ctx, accountID, userID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteUser(ctx, accountID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteUser", reflect.TypeOf((*MockStore)(nil).DeleteUser), ctx, accountID, userID) } @@ -840,7 +846,7 @@ func (m *MockStore) DeleteUserInvite(ctx context.Context, inviteID string) error } // DeleteUserInvite indicates an expected call of DeleteUserInvite. -func (mr *MockStoreMockRecorder) DeleteUserInvite(ctx, inviteID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteUserInvite(ctx, inviteID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteUserInvite", reflect.TypeOf((*MockStore)(nil).DeleteUserInvite), ctx, inviteID) } @@ -854,7 +860,7 @@ func (m *MockStore) DeleteZone(ctx context.Context, accountID, zoneID string) er } // DeleteZone indicates an expected call of DeleteZone. -func (mr *MockStoreMockRecorder) DeleteZone(ctx, accountID, zoneID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteZone(ctx, accountID, zoneID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteZone", reflect.TypeOf((*MockStore)(nil).DeleteZone), ctx, accountID, zoneID) } @@ -868,7 +874,7 @@ func (m *MockStore) DeleteZoneDNSRecords(ctx context.Context, accountID, zoneID } // DeleteZoneDNSRecords indicates an expected call of DeleteZoneDNSRecords. -func (mr *MockStoreMockRecorder) DeleteZoneDNSRecords(ctx, accountID, zoneID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DeleteZoneDNSRecords(ctx, accountID, zoneID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteZoneDNSRecords", reflect.TypeOf((*MockStore)(nil).DeleteZoneDNSRecords), ctx, accountID, zoneID) } @@ -883,7 +889,7 @@ func (m *MockStore) DisconnectAllProxies(ctx context.Context) (int64, error) { } // DisconnectAllProxies indicates an expected call of DisconnectAllProxies. -func (mr *MockStoreMockRecorder) DisconnectAllProxies(ctx interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DisconnectAllProxies(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DisconnectAllProxies", reflect.TypeOf((*MockStore)(nil).DisconnectAllProxies), ctx) } @@ -897,24 +903,24 @@ func (m *MockStore) DisconnectProxy(ctx context.Context, proxyID, sessionID stri } // DisconnectProxy indicates an expected call of DisconnectProxy. -func (mr *MockStoreMockRecorder) DisconnectProxy(ctx, proxyID, sessionID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) DisconnectProxy(ctx, proxyID, sessionID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DisconnectProxy", reflect.TypeOf((*MockStore)(nil).DisconnectProxy), ctx, proxyID, sessionID) } // EphemeralServiceExists mocks base method. -func (m *MockStore) EphemeralServiceExists(ctx context.Context, lockStrength LockingStrength, accountID, peerID, domain string) (bool, error) { +func (m *MockStore) EphemeralServiceExists(ctx context.Context, lockStrength LockingStrength, accountID, peerID, arg4 string) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EphemeralServiceExists", ctx, lockStrength, accountID, peerID, domain) + ret := m.ctrl.Call(m, "EphemeralServiceExists", ctx, lockStrength, accountID, peerID, arg4) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // EphemeralServiceExists indicates an expected call of EphemeralServiceExists. -func (mr *MockStoreMockRecorder) EphemeralServiceExists(ctx, lockStrength, accountID, peerID, domain interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) EphemeralServiceExists(ctx, lockStrength, accountID, peerID, arg4 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EphemeralServiceExists", reflect.TypeOf((*MockStore)(nil).EphemeralServiceExists), ctx, lockStrength, accountID, peerID, domain) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EphemeralServiceExists", reflect.TypeOf((*MockStore)(nil).EphemeralServiceExists), ctx, lockStrength, accountID, peerID, arg4) } // ExecuteInTransaction mocks base method. @@ -926,7 +932,7 @@ func (m *MockStore) ExecuteInTransaction(ctx context.Context, f func(Store) erro } // ExecuteInTransaction indicates an expected call of ExecuteInTransaction. -func (mr *MockStoreMockRecorder) ExecuteInTransaction(ctx, f interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) ExecuteInTransaction(ctx, f any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteInTransaction", reflect.TypeOf((*MockStore)(nil).ExecuteInTransaction), ctx, f) } @@ -941,7 +947,7 @@ func (m *MockStore) GetAccount(ctx context.Context, accountID string) (*types3.A } // GetAccount indicates an expected call of GetAccount. -func (mr *MockStoreMockRecorder) GetAccount(ctx, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccount(ctx, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockStore)(nil).GetAccount), ctx, accountID) } @@ -957,7 +963,7 @@ func (m *MockStore) GetAccountAccessLogs(ctx context.Context, lockStrength Locki } // GetAccountAccessLogs indicates an expected call of GetAccountAccessLogs. -func (mr *MockStoreMockRecorder) GetAccountAccessLogs(ctx, lockStrength, accountID, filter interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountAccessLogs(ctx, lockStrength, accountID, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountAccessLogs", reflect.TypeOf((*MockStore)(nil).GetAccountAccessLogs), ctx, lockStrength, accountID, filter) } @@ -972,7 +978,7 @@ func (m *MockStore) GetAccountAgentNetworkBudgetRules(ctx context.Context, lockS } // GetAccountAgentNetworkBudgetRules indicates an expected call of GetAccountAgentNetworkBudgetRules. -func (mr *MockStoreMockRecorder) GetAccountAgentNetworkBudgetRules(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountAgentNetworkBudgetRules(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountAgentNetworkBudgetRules", reflect.TypeOf((*MockStore)(nil).GetAccountAgentNetworkBudgetRules), ctx, lockStrength, accountID) } @@ -987,7 +993,7 @@ func (m *MockStore) GetAccountAgentNetworkGuardrails(ctx context.Context, lockSt } // GetAccountAgentNetworkGuardrails indicates an expected call of GetAccountAgentNetworkGuardrails. -func (mr *MockStoreMockRecorder) GetAccountAgentNetworkGuardrails(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountAgentNetworkGuardrails(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountAgentNetworkGuardrails", reflect.TypeOf((*MockStore)(nil).GetAccountAgentNetworkGuardrails), ctx, lockStrength, accountID) } @@ -1002,7 +1008,7 @@ func (m *MockStore) GetAccountAgentNetworkPolicies(ctx context.Context, lockStre } // GetAccountAgentNetworkPolicies indicates an expected call of GetAccountAgentNetworkPolicies. -func (mr *MockStoreMockRecorder) GetAccountAgentNetworkPolicies(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountAgentNetworkPolicies(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountAgentNetworkPolicies", reflect.TypeOf((*MockStore)(nil).GetAccountAgentNetworkPolicies), ctx, lockStrength, accountID) } @@ -1017,7 +1023,7 @@ func (m *MockStore) GetAccountAgentNetworkProviders(ctx context.Context, lockStr } // GetAccountAgentNetworkProviders indicates an expected call of GetAccountAgentNetworkProviders. -func (mr *MockStoreMockRecorder) GetAccountAgentNetworkProviders(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountAgentNetworkProviders(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountAgentNetworkProviders", reflect.TypeOf((*MockStore)(nil).GetAccountAgentNetworkProviders), ctx, lockStrength, accountID) } @@ -1032,7 +1038,7 @@ func (m *MockStore) GetAccountByPeerID(ctx context.Context, peerID string) (*typ } // GetAccountByPeerID indicates an expected call of GetAccountByPeerID. -func (mr *MockStoreMockRecorder) GetAccountByPeerID(ctx, peerID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountByPeerID(ctx, peerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountByPeerID", reflect.TypeOf((*MockStore)(nil).GetAccountByPeerID), ctx, peerID) } @@ -1047,24 +1053,24 @@ func (m *MockStore) GetAccountByPeerPubKey(ctx context.Context, peerKey string) } // GetAccountByPeerPubKey indicates an expected call of GetAccountByPeerPubKey. -func (mr *MockStoreMockRecorder) GetAccountByPeerPubKey(ctx, peerKey interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountByPeerPubKey(ctx, peerKey any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountByPeerPubKey", reflect.TypeOf((*MockStore)(nil).GetAccountByPeerPubKey), ctx, peerKey) } // GetAccountByPrivateDomain mocks base method. -func (m *MockStore) GetAccountByPrivateDomain(ctx context.Context, domain string) (*types3.Account, error) { +func (m *MockStore) GetAccountByPrivateDomain(ctx context.Context, arg1 string) (*types3.Account, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAccountByPrivateDomain", ctx, domain) + ret := m.ctrl.Call(m, "GetAccountByPrivateDomain", ctx, arg1) ret0, _ := ret[0].(*types3.Account) ret1, _ := ret[1].(error) return ret0, ret1 } // GetAccountByPrivateDomain indicates an expected call of GetAccountByPrivateDomain. -func (mr *MockStoreMockRecorder) GetAccountByPrivateDomain(ctx, domain interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountByPrivateDomain(ctx, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountByPrivateDomain", reflect.TypeOf((*MockStore)(nil).GetAccountByPrivateDomain), ctx, domain) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountByPrivateDomain", reflect.TypeOf((*MockStore)(nil).GetAccountByPrivateDomain), ctx, arg1) } // GetAccountBySetupKey mocks base method. @@ -1077,7 +1083,7 @@ func (m *MockStore) GetAccountBySetupKey(ctx context.Context, setupKey string) ( } // GetAccountBySetupKey indicates an expected call of GetAccountBySetupKey. -func (mr *MockStoreMockRecorder) GetAccountBySetupKey(ctx, setupKey interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountBySetupKey(ctx, setupKey any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountBySetupKey", reflect.TypeOf((*MockStore)(nil).GetAccountBySetupKey), ctx, setupKey) } @@ -1092,7 +1098,7 @@ func (m *MockStore) GetAccountByUser(ctx context.Context, userID string) (*types } // GetAccountByUser indicates an expected call of GetAccountByUser. -func (mr *MockStoreMockRecorder) GetAccountByUser(ctx, userID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountByUser(ctx, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountByUser", reflect.TypeOf((*MockStore)(nil).GetAccountByUser), ctx, userID) } @@ -1107,7 +1113,7 @@ func (m *MockStore) GetAccountCreatedBy(ctx context.Context, lockStrength Lockin } // GetAccountCreatedBy indicates an expected call of GetAccountCreatedBy. -func (mr *MockStoreMockRecorder) GetAccountCreatedBy(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountCreatedBy(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountCreatedBy", reflect.TypeOf((*MockStore)(nil).GetAccountCreatedBy), ctx, lockStrength, accountID) } @@ -1122,7 +1128,7 @@ func (m *MockStore) GetAccountDNSSettings(ctx context.Context, lockStrength Lock } // GetAccountDNSSettings indicates an expected call of GetAccountDNSSettings. -func (mr *MockStoreMockRecorder) GetAccountDNSSettings(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountDNSSettings(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountDNSSettings", reflect.TypeOf((*MockStore)(nil).GetAccountDNSSettings), ctx, lockStrength, accountID) } @@ -1138,7 +1144,7 @@ func (m *MockStore) GetAccountDomainAndCategory(ctx context.Context, lockStrengt } // GetAccountDomainAndCategory indicates an expected call of GetAccountDomainAndCategory. -func (mr *MockStoreMockRecorder) GetAccountDomainAndCategory(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountDomainAndCategory(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountDomainAndCategory", reflect.TypeOf((*MockStore)(nil).GetAccountDomainAndCategory), ctx, lockStrength, accountID) } @@ -1153,7 +1159,7 @@ func (m *MockStore) GetAccountGroupPeers(ctx context.Context, lockStrength Locki } // GetAccountGroupPeers indicates an expected call of GetAccountGroupPeers. -func (mr *MockStoreMockRecorder) GetAccountGroupPeers(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountGroupPeers(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountGroupPeers", reflect.TypeOf((*MockStore)(nil).GetAccountGroupPeers), ctx, lockStrength, accountID) } @@ -1168,7 +1174,7 @@ func (m *MockStore) GetAccountGroups(ctx context.Context, lockStrength LockingSt } // GetAccountGroups indicates an expected call of GetAccountGroups. -func (mr *MockStoreMockRecorder) GetAccountGroups(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountGroups(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountGroups", reflect.TypeOf((*MockStore)(nil).GetAccountGroups), ctx, lockStrength, accountID) } @@ -1183,7 +1189,7 @@ func (m *MockStore) GetAccountIDByPeerID(ctx context.Context, lockStrength Locki } // GetAccountIDByPeerID indicates an expected call of GetAccountIDByPeerID. -func (mr *MockStoreMockRecorder) GetAccountIDByPeerID(ctx, lockStrength, peerID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountIDByPeerID(ctx, lockStrength, peerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountIDByPeerID", reflect.TypeOf((*MockStore)(nil).GetAccountIDByPeerID), ctx, lockStrength, peerID) } @@ -1198,24 +1204,24 @@ func (m *MockStore) GetAccountIDByPeerPubKey(ctx context.Context, peerKey string } // GetAccountIDByPeerPubKey indicates an expected call of GetAccountIDByPeerPubKey. -func (mr *MockStoreMockRecorder) GetAccountIDByPeerPubKey(ctx, peerKey interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountIDByPeerPubKey(ctx, peerKey any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountIDByPeerPubKey", reflect.TypeOf((*MockStore)(nil).GetAccountIDByPeerPubKey), ctx, peerKey) } // GetAccountIDByPrivateDomain mocks base method. -func (m *MockStore) GetAccountIDByPrivateDomain(ctx context.Context, lockStrength LockingStrength, domain string) (string, error) { +func (m *MockStore) GetAccountIDByPrivateDomain(ctx context.Context, lockStrength LockingStrength, arg2 string) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAccountIDByPrivateDomain", ctx, lockStrength, domain) + ret := m.ctrl.Call(m, "GetAccountIDByPrivateDomain", ctx, lockStrength, arg2) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetAccountIDByPrivateDomain indicates an expected call of GetAccountIDByPrivateDomain. -func (mr *MockStoreMockRecorder) GetAccountIDByPrivateDomain(ctx, lockStrength, domain interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountIDByPrivateDomain(ctx, lockStrength, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountIDByPrivateDomain", reflect.TypeOf((*MockStore)(nil).GetAccountIDByPrivateDomain), ctx, lockStrength, domain) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountIDByPrivateDomain", reflect.TypeOf((*MockStore)(nil).GetAccountIDByPrivateDomain), ctx, lockStrength, arg2) } // GetAccountIDBySetupKey mocks base method. @@ -1228,7 +1234,7 @@ func (m *MockStore) GetAccountIDBySetupKey(ctx context.Context, peerKey string) } // GetAccountIDBySetupKey indicates an expected call of GetAccountIDBySetupKey. -func (mr *MockStoreMockRecorder) GetAccountIDBySetupKey(ctx, peerKey interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountIDBySetupKey(ctx, peerKey any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountIDBySetupKey", reflect.TypeOf((*MockStore)(nil).GetAccountIDBySetupKey), ctx, peerKey) } @@ -1243,7 +1249,7 @@ func (m *MockStore) GetAccountIDByUserID(ctx context.Context, lockStrength Locki } // GetAccountIDByUserID indicates an expected call of GetAccountIDByUserID. -func (mr *MockStoreMockRecorder) GetAccountIDByUserID(ctx, lockStrength, userID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountIDByUserID(ctx, lockStrength, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountIDByUserID", reflect.TypeOf((*MockStore)(nil).GetAccountIDByUserID), ctx, lockStrength, userID) } @@ -1258,7 +1264,7 @@ func (m *MockStore) GetAccountMeta(ctx context.Context, lockStrength LockingStre } // GetAccountMeta indicates an expected call of GetAccountMeta. -func (mr *MockStoreMockRecorder) GetAccountMeta(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountMeta(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountMeta", reflect.TypeOf((*MockStore)(nil).GetAccountMeta), ctx, lockStrength, accountID) } @@ -1273,7 +1279,7 @@ func (m *MockStore) GetAccountNameServerGroups(ctx context.Context, lockStrength } // GetAccountNameServerGroups indicates an expected call of GetAccountNameServerGroups. -func (mr *MockStoreMockRecorder) GetAccountNameServerGroups(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountNameServerGroups(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountNameServerGroups", reflect.TypeOf((*MockStore)(nil).GetAccountNameServerGroups), ctx, lockStrength, accountID) } @@ -1288,7 +1294,7 @@ func (m *MockStore) GetAccountNetwork(ctx context.Context, lockStrength LockingS } // GetAccountNetwork indicates an expected call of GetAccountNetwork. -func (mr *MockStoreMockRecorder) GetAccountNetwork(ctx, lockStrength, accountId interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountNetwork(ctx, lockStrength, accountId any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountNetwork", reflect.TypeOf((*MockStore)(nil).GetAccountNetwork), ctx, lockStrength, accountId) } @@ -1303,7 +1309,7 @@ func (m *MockStore) GetAccountNetworks(ctx context.Context, lockStrength Locking } // GetAccountNetworks indicates an expected call of GetAccountNetworks. -func (mr *MockStoreMockRecorder) GetAccountNetworks(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountNetworks(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountNetworks", reflect.TypeOf((*MockStore)(nil).GetAccountNetworks), ctx, lockStrength, accountID) } @@ -1318,7 +1324,7 @@ func (m *MockStore) GetAccountOnboarding(ctx context.Context, accountID string) } // GetAccountOnboarding indicates an expected call of GetAccountOnboarding. -func (mr *MockStoreMockRecorder) GetAccountOnboarding(ctx, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountOnboarding(ctx, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountOnboarding", reflect.TypeOf((*MockStore)(nil).GetAccountOnboarding), ctx, accountID) } @@ -1333,7 +1339,7 @@ func (m *MockStore) GetAccountOwner(ctx context.Context, lockStrength LockingStr } // GetAccountOwner indicates an expected call of GetAccountOwner. -func (mr *MockStoreMockRecorder) GetAccountOwner(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountOwner(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountOwner", reflect.TypeOf((*MockStore)(nil).GetAccountOwner), ctx, lockStrength, accountID) } @@ -1348,7 +1354,7 @@ func (m *MockStore) GetAccountPeers(ctx context.Context, lockStrength LockingStr } // GetAccountPeers indicates an expected call of GetAccountPeers. -func (mr *MockStoreMockRecorder) GetAccountPeers(ctx, lockStrength, accountID, nameFilter, ipFilter interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountPeers(ctx, lockStrength, accountID, nameFilter, ipFilter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountPeers", reflect.TypeOf((*MockStore)(nil).GetAccountPeers), ctx, lockStrength, accountID, nameFilter, ipFilter) } @@ -1363,7 +1369,7 @@ func (m *MockStore) GetAccountPeersWithExpiration(ctx context.Context, lockStren } // GetAccountPeersWithExpiration indicates an expected call of GetAccountPeersWithExpiration. -func (mr *MockStoreMockRecorder) GetAccountPeersWithExpiration(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountPeersWithExpiration(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountPeersWithExpiration", reflect.TypeOf((*MockStore)(nil).GetAccountPeersWithExpiration), ctx, lockStrength, accountID) } @@ -1378,7 +1384,7 @@ func (m *MockStore) GetAccountPeersWithInactivity(ctx context.Context, lockStren } // GetAccountPeersWithInactivity indicates an expected call of GetAccountPeersWithInactivity. -func (mr *MockStoreMockRecorder) GetAccountPeersWithInactivity(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountPeersWithInactivity(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountPeersWithInactivity", reflect.TypeOf((*MockStore)(nil).GetAccountPeersWithInactivity), ctx, lockStrength, accountID) } @@ -1393,7 +1399,7 @@ func (m *MockStore) GetAccountPolicies(ctx context.Context, lockStrength Locking } // GetAccountPolicies indicates an expected call of GetAccountPolicies. -func (mr *MockStoreMockRecorder) GetAccountPolicies(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountPolicies(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountPolicies", reflect.TypeOf((*MockStore)(nil).GetAccountPolicies), ctx, lockStrength, accountID) } @@ -1408,7 +1414,7 @@ func (m *MockStore) GetAccountPostureChecks(ctx context.Context, lockStrength Lo } // GetAccountPostureChecks indicates an expected call of GetAccountPostureChecks. -func (mr *MockStoreMockRecorder) GetAccountPostureChecks(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountPostureChecks(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountPostureChecks", reflect.TypeOf((*MockStore)(nil).GetAccountPostureChecks), ctx, lockStrength, accountID) } @@ -1423,7 +1429,7 @@ func (m *MockStore) GetAccountRoutes(ctx context.Context, lockStrength LockingSt } // GetAccountRoutes indicates an expected call of GetAccountRoutes. -func (mr *MockStoreMockRecorder) GetAccountRoutes(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountRoutes(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountRoutes", reflect.TypeOf((*MockStore)(nil).GetAccountRoutes), ctx, lockStrength, accountID) } @@ -1438,7 +1444,7 @@ func (m *MockStore) GetAccountServices(ctx context.Context, lockStrength Locking } // GetAccountServices indicates an expected call of GetAccountServices. -func (mr *MockStoreMockRecorder) GetAccountServices(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountServices(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountServices", reflect.TypeOf((*MockStore)(nil).GetAccountServices), ctx, lockStrength, accountID) } @@ -1453,7 +1459,7 @@ func (m *MockStore) GetAccountSettings(ctx context.Context, lockStrength Locking } // GetAccountSettings indicates an expected call of GetAccountSettings. -func (mr *MockStoreMockRecorder) GetAccountSettings(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountSettings(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountSettings", reflect.TypeOf((*MockStore)(nil).GetAccountSettings), ctx, lockStrength, accountID) } @@ -1468,7 +1474,7 @@ func (m *MockStore) GetAccountSetupKeys(ctx context.Context, lockStrength Lockin } // GetAccountSetupKeys indicates an expected call of GetAccountSetupKeys. -func (mr *MockStoreMockRecorder) GetAccountSetupKeys(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountSetupKeys(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountSetupKeys", reflect.TypeOf((*MockStore)(nil).GetAccountSetupKeys), ctx, lockStrength, accountID) } @@ -1483,7 +1489,7 @@ func (m *MockStore) GetAccountUserInvites(ctx context.Context, lockStrength Lock } // GetAccountUserInvites indicates an expected call of GetAccountUserInvites. -func (mr *MockStoreMockRecorder) GetAccountUserInvites(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountUserInvites(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountUserInvites", reflect.TypeOf((*MockStore)(nil).GetAccountUserInvites), ctx, lockStrength, accountID) } @@ -1498,7 +1504,7 @@ func (m *MockStore) GetAccountUsers(ctx context.Context, lockStrength LockingStr } // GetAccountUsers indicates an expected call of GetAccountUsers. -func (mr *MockStoreMockRecorder) GetAccountUsers(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountUsers(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountUsers", reflect.TypeOf((*MockStore)(nil).GetAccountUsers), ctx, lockStrength, accountID) } @@ -1513,7 +1519,7 @@ func (m *MockStore) GetAccountZones(ctx context.Context, lockStrength LockingStr } // GetAccountZones indicates an expected call of GetAccountZones. -func (mr *MockStoreMockRecorder) GetAccountZones(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountZones(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountZones", reflect.TypeOf((*MockStore)(nil).GetAccountZones), ctx, lockStrength, accountID) } @@ -1528,7 +1534,7 @@ func (m *MockStore) GetAccountsCounter(ctx context.Context) (int64, error) { } // GetAccountsCounter indicates an expected call of GetAccountsCounter. -func (mr *MockStoreMockRecorder) GetAccountsCounter(ctx interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAccountsCounter(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountsCounter", reflect.TypeOf((*MockStore)(nil).GetAccountsCounter), ctx) } @@ -1543,7 +1549,7 @@ func (m *MockStore) GetActiveProxyClusterAddresses(ctx context.Context) ([]strin } // GetActiveProxyClusterAddresses indicates an expected call of GetActiveProxyClusterAddresses. -func (mr *MockStoreMockRecorder) GetActiveProxyClusterAddresses(ctx interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetActiveProxyClusterAddresses(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveProxyClusterAddresses", reflect.TypeOf((*MockStore)(nil).GetActiveProxyClusterAddresses), ctx) } @@ -1558,7 +1564,7 @@ func (m *MockStore) GetActiveProxyClusterAddressesForAccount(ctx context.Context } // GetActiveProxyClusterAddressesForAccount indicates an expected call of GetActiveProxyClusterAddressesForAccount. -func (mr *MockStoreMockRecorder) GetActiveProxyClusterAddressesForAccount(ctx, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetActiveProxyClusterAddressesForAccount(ctx, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveProxyClusterAddressesForAccount", reflect.TypeOf((*MockStore)(nil).GetActiveProxyClusterAddressesForAccount), ctx, accountID) } @@ -1574,7 +1580,7 @@ func (m *MockStore) GetAgentNetworkAccessLogSessions(ctx context.Context, lockSt } // GetAgentNetworkAccessLogSessions indicates an expected call of GetAgentNetworkAccessLogSessions. -func (mr *MockStoreMockRecorder) GetAgentNetworkAccessLogSessions(ctx, lockStrength, accountID, filter interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAgentNetworkAccessLogSessions(ctx, lockStrength, accountID, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkAccessLogSessions", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkAccessLogSessions), ctx, lockStrength, accountID, filter) } @@ -1590,7 +1596,7 @@ func (m *MockStore) GetAgentNetworkAccessLogs(ctx context.Context, lockStrength } // GetAgentNetworkAccessLogs indicates an expected call of GetAgentNetworkAccessLogs. -func (mr *MockStoreMockRecorder) GetAgentNetworkAccessLogs(ctx, lockStrength, accountID, filter interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAgentNetworkAccessLogs(ctx, lockStrength, accountID, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkAccessLogs", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkAccessLogs), ctx, lockStrength, accountID, filter) } @@ -1605,7 +1611,7 @@ func (m *MockStore) GetAgentNetworkBudgetRuleByID(ctx context.Context, lockStren } // GetAgentNetworkBudgetRuleByID indicates an expected call of GetAgentNetworkBudgetRuleByID. -func (mr *MockStoreMockRecorder) GetAgentNetworkBudgetRuleByID(ctx, lockStrength, accountID, ruleID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAgentNetworkBudgetRuleByID(ctx, lockStrength, accountID, ruleID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkBudgetRuleByID", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkBudgetRuleByID), ctx, lockStrength, accountID, ruleID) } @@ -1620,7 +1626,7 @@ func (m *MockStore) GetAgentNetworkConsumption(ctx context.Context, lockStrength } // GetAgentNetworkConsumption indicates an expected call of GetAgentNetworkConsumption. -func (mr *MockStoreMockRecorder) GetAgentNetworkConsumption(ctx, lockStrength, accountID, kind, dimID, windowSeconds, windowStart interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAgentNetworkConsumption(ctx, lockStrength, accountID, kind, dimID, windowSeconds, windowStart any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkConsumption", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkConsumption), ctx, lockStrength, accountID, kind, dimID, windowSeconds, windowStart) } @@ -1635,7 +1641,7 @@ func (m *MockStore) GetAgentNetworkConsumptionBatch(ctx context.Context, lockStr } // GetAgentNetworkConsumptionBatch indicates an expected call of GetAgentNetworkConsumptionBatch. -func (mr *MockStoreMockRecorder) GetAgentNetworkConsumptionBatch(ctx, lockStrength, accountID, keys interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAgentNetworkConsumptionBatch(ctx, lockStrength, accountID, keys any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkConsumptionBatch", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkConsumptionBatch), ctx, lockStrength, accountID, keys) } @@ -1650,7 +1656,7 @@ func (m *MockStore) GetAgentNetworkGuardrailByID(ctx context.Context, lockStreng } // GetAgentNetworkGuardrailByID indicates an expected call of GetAgentNetworkGuardrailByID. -func (mr *MockStoreMockRecorder) GetAgentNetworkGuardrailByID(ctx, lockStrength, accountID, guardrailID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAgentNetworkGuardrailByID(ctx, lockStrength, accountID, guardrailID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkGuardrailByID", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkGuardrailByID), ctx, lockStrength, accountID, guardrailID) } @@ -1665,7 +1671,7 @@ func (m *MockStore) GetAgentNetworkMetrics(ctx context.Context) (AgentNetworkMet } // GetAgentNetworkMetrics indicates an expected call of GetAgentNetworkMetrics. -func (mr *MockStoreMockRecorder) GetAgentNetworkMetrics(ctx interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAgentNetworkMetrics(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkMetrics", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkMetrics), ctx) } @@ -1680,7 +1686,7 @@ func (m *MockStore) GetAgentNetworkPolicyByID(ctx context.Context, lockStrength } // GetAgentNetworkPolicyByID indicates an expected call of GetAgentNetworkPolicyByID. -func (mr *MockStoreMockRecorder) GetAgentNetworkPolicyByID(ctx, lockStrength, accountID, policyID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAgentNetworkPolicyByID(ctx, lockStrength, accountID, policyID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkPolicyByID", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkPolicyByID), ctx, lockStrength, accountID, policyID) } @@ -1695,7 +1701,7 @@ func (m *MockStore) GetAgentNetworkProviderByID(ctx context.Context, lockStrengt } // GetAgentNetworkProviderByID indicates an expected call of GetAgentNetworkProviderByID. -func (mr *MockStoreMockRecorder) GetAgentNetworkProviderByID(ctx, lockStrength, accountID, providerID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAgentNetworkProviderByID(ctx, lockStrength, accountID, providerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkProviderByID", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkProviderByID), ctx, lockStrength, accountID, providerID) } @@ -1710,24 +1716,24 @@ func (m *MockStore) GetAgentNetworkSettings(ctx context.Context, lockStrength Lo } // GetAgentNetworkSettings indicates an expected call of GetAgentNetworkSettings. -func (mr *MockStoreMockRecorder) GetAgentNetworkSettings(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAgentNetworkSettings(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkSettings", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkSettings), ctx, lockStrength, accountID) } // GetAgentNetworkSettingsByDomain mocks base method. -func (m *MockStore) GetAgentNetworkSettingsByDomain(ctx context.Context, lockStrength LockingStrength, domain string) (*types.Settings, error) { +func (m *MockStore) GetAgentNetworkSettingsByDomain(ctx context.Context, lockStrength LockingStrength, arg2 string) (*types.Settings, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAgentNetworkSettingsByDomain", ctx, lockStrength, domain) + ret := m.ctrl.Call(m, "GetAgentNetworkSettingsByDomain", ctx, lockStrength, arg2) ret0, _ := ret[0].(*types.Settings) ret1, _ := ret[1].(error) return ret0, ret1 } // GetAgentNetworkSettingsByDomain indicates an expected call of GetAgentNetworkSettingsByDomain. -func (mr *MockStoreMockRecorder) GetAgentNetworkSettingsByDomain(ctx, lockStrength, domain interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAgentNetworkSettingsByDomain(ctx, lockStrength, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkSettingsByDomain", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkSettingsByDomain), ctx, lockStrength, domain) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkSettingsByDomain", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkSettingsByDomain), ctx, lockStrength, arg2) } // GetAgentNetworkSettingsByProxyAddress mocks base method. @@ -1740,7 +1746,7 @@ func (m *MockStore) GetAgentNetworkSettingsByProxyAddress(ctx context.Context, l } // GetAgentNetworkSettingsByProxyAddress indicates an expected call of GetAgentNetworkSettingsByProxyAddress. -func (mr *MockStoreMockRecorder) GetAgentNetworkSettingsByProxyAddress(ctx, lockStrength, proxyAddress interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAgentNetworkSettingsByProxyAddress(ctx, lockStrength, proxyAddress any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkSettingsByProxyAddress", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkSettingsByProxyAddress), ctx, lockStrength, proxyAddress) } @@ -1755,7 +1761,7 @@ func (m *MockStore) GetAgentNetworkUsageRows(ctx context.Context, lockStrength L } // GetAgentNetworkUsageRows indicates an expected call of GetAgentNetworkUsageRows. -func (mr *MockStoreMockRecorder) GetAgentNetworkUsageRows(ctx, lockStrength, accountID, filter interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAgentNetworkUsageRows(ctx, lockStrength, accountID, filter any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentNetworkUsageRows", reflect.TypeOf((*MockStore)(nil).GetAgentNetworkUsageRows), ctx, lockStrength, accountID, filter) } @@ -1769,7 +1775,7 @@ func (m *MockStore) GetAllAccounts(ctx context.Context) []*types3.Account { } // GetAllAccounts indicates an expected call of GetAllAccounts. -func (mr *MockStoreMockRecorder) GetAllAccounts(ctx interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAllAccounts(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllAccounts", reflect.TypeOf((*MockStore)(nil).GetAllAccounts), ctx) } @@ -1784,7 +1790,7 @@ func (m *MockStore) GetAllAgentNetworkProviders(ctx context.Context, lockStrengt } // GetAllAgentNetworkProviders indicates an expected call of GetAllAgentNetworkProviders. -func (mr *MockStoreMockRecorder) GetAllAgentNetworkProviders(ctx, lockStrength interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAllAgentNetworkProviders(ctx, lockStrength any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllAgentNetworkProviders", reflect.TypeOf((*MockStore)(nil).GetAllAgentNetworkProviders), ctx, lockStrength) } @@ -1799,7 +1805,7 @@ func (m *MockStore) GetAllAgentNetworkSettings(ctx context.Context, lockStrength } // GetAllAgentNetworkSettings indicates an expected call of GetAllAgentNetworkSettings. -func (mr *MockStoreMockRecorder) GetAllAgentNetworkSettings(ctx, lockStrength interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAllAgentNetworkSettings(ctx, lockStrength any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllAgentNetworkSettings", reflect.TypeOf((*MockStore)(nil).GetAllAgentNetworkSettings), ctx, lockStrength) } @@ -1814,7 +1820,7 @@ func (m *MockStore) GetAllEphemeralPeers(ctx context.Context, lockStrength Locki } // GetAllEphemeralPeers indicates an expected call of GetAllEphemeralPeers. -func (mr *MockStoreMockRecorder) GetAllEphemeralPeers(ctx, lockStrength interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAllEphemeralPeers(ctx, lockStrength any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllEphemeralPeers", reflect.TypeOf((*MockStore)(nil).GetAllEphemeralPeers), ctx, lockStrength) } @@ -1829,7 +1835,7 @@ func (m *MockStore) GetAllProxies(ctx context.Context) ([]*proxy.Proxy, error) { } // GetAllProxies indicates an expected call of GetAllProxies. -func (mr *MockStoreMockRecorder) GetAllProxies(ctx interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAllProxies(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllProxies", reflect.TypeOf((*MockStore)(nil).GetAllProxies), ctx) } @@ -1844,7 +1850,7 @@ func (m *MockStore) GetAllProxyAccessTokens(ctx context.Context, lockStrength Lo } // GetAllProxyAccessTokens indicates an expected call of GetAllProxyAccessTokens. -func (mr *MockStoreMockRecorder) GetAllProxyAccessTokens(ctx, lockStrength interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAllProxyAccessTokens(ctx, lockStrength any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllProxyAccessTokens", reflect.TypeOf((*MockStore)(nil).GetAllProxyAccessTokens), ctx, lockStrength) } @@ -1859,7 +1865,7 @@ func (m *MockStore) GetAnyAccountID(ctx context.Context) (string, error) { } // GetAnyAccountID indicates an expected call of GetAnyAccountID. -func (mr *MockStoreMockRecorder) GetAnyAccountID(ctx interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetAnyAccountID(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAnyAccountID", reflect.TypeOf((*MockStore)(nil).GetAnyAccountID), ctx) } @@ -1873,7 +1879,7 @@ func (m *MockStore) GetClusterRequireSubdomain(ctx context.Context, clusterAddr } // GetClusterRequireSubdomain indicates an expected call of GetClusterRequireSubdomain. -func (mr *MockStoreMockRecorder) GetClusterRequireSubdomain(ctx, clusterAddr interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetClusterRequireSubdomain(ctx, clusterAddr any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClusterRequireSubdomain", reflect.TypeOf((*MockStore)(nil).GetClusterRequireSubdomain), ctx, clusterAddr) } @@ -1887,7 +1893,7 @@ func (m *MockStore) GetClusterSupportsCrowdSec(ctx context.Context, clusterAddr } // GetClusterSupportsCrowdSec indicates an expected call of GetClusterSupportsCrowdSec. -func (mr *MockStoreMockRecorder) GetClusterSupportsCrowdSec(ctx, clusterAddr interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetClusterSupportsCrowdSec(ctx, clusterAddr any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClusterSupportsCrowdSec", reflect.TypeOf((*MockStore)(nil).GetClusterSupportsCrowdSec), ctx, clusterAddr) } @@ -1901,7 +1907,7 @@ func (m *MockStore) GetClusterSupportsCustomPorts(ctx context.Context, clusterAd } // GetClusterSupportsCustomPorts indicates an expected call of GetClusterSupportsCustomPorts. -func (mr *MockStoreMockRecorder) GetClusterSupportsCustomPorts(ctx, clusterAddr interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetClusterSupportsCustomPorts(ctx, clusterAddr any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClusterSupportsCustomPorts", reflect.TypeOf((*MockStore)(nil).GetClusterSupportsCustomPorts), ctx, clusterAddr) } @@ -1915,7 +1921,7 @@ func (m *MockStore) GetClusterSupportsPrivate(ctx context.Context, clusterAddr s } // GetClusterSupportsPrivate indicates an expected call of GetClusterSupportsPrivate. -func (mr *MockStoreMockRecorder) GetClusterSupportsPrivate(ctx, clusterAddr interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetClusterSupportsPrivate(ctx, clusterAddr any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClusterSupportsPrivate", reflect.TypeOf((*MockStore)(nil).GetClusterSupportsPrivate), ctx, clusterAddr) } @@ -1930,7 +1936,7 @@ func (m *MockStore) GetCustomDomain(ctx context.Context, accountID, domainID str } // GetCustomDomain indicates an expected call of GetCustomDomain. -func (mr *MockStoreMockRecorder) GetCustomDomain(ctx, accountID, domainID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetCustomDomain(ctx, accountID, domainID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCustomDomain", reflect.TypeOf((*MockStore)(nil).GetCustomDomain), ctx, accountID, domainID) } @@ -1946,7 +1952,7 @@ func (m *MockStore) GetCustomDomainsCounts(ctx context.Context) (int64, int64, e } // GetCustomDomainsCounts indicates an expected call of GetCustomDomainsCounts. -func (mr *MockStoreMockRecorder) GetCustomDomainsCounts(ctx interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetCustomDomainsCounts(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCustomDomainsCounts", reflect.TypeOf((*MockStore)(nil).GetCustomDomainsCounts), ctx) } @@ -1961,7 +1967,7 @@ func (m *MockStore) GetDNSRecordByID(ctx context.Context, lockStrength LockingSt } // GetDNSRecordByID indicates an expected call of GetDNSRecordByID. -func (mr *MockStoreMockRecorder) GetDNSRecordByID(ctx, lockStrength, accountID, zoneID, recordID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetDNSRecordByID(ctx, lockStrength, accountID, zoneID, recordID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDNSRecordByID", reflect.TypeOf((*MockStore)(nil).GetDNSRecordByID), ctx, lockStrength, accountID, zoneID, recordID) } @@ -1976,7 +1982,7 @@ func (m *MockStore) GetEmbeddedProxyPeerIDsByCluster(ctx context.Context, accoun } // GetEmbeddedProxyPeerIDsByCluster indicates an expected call of GetEmbeddedProxyPeerIDsByCluster. -func (mr *MockStoreMockRecorder) GetEmbeddedProxyPeerIDsByCluster(ctx, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetEmbeddedProxyPeerIDsByCluster(ctx, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEmbeddedProxyPeerIDsByCluster", reflect.TypeOf((*MockStore)(nil).GetEmbeddedProxyPeerIDsByCluster), ctx, accountID) } @@ -1991,7 +1997,7 @@ func (m *MockStore) GetExpiredEphemeralServices(ctx context.Context, ttl time.Du } // GetExpiredEphemeralServices indicates an expected call of GetExpiredEphemeralServices. -func (mr *MockStoreMockRecorder) GetExpiredEphemeralServices(ctx, ttl, limit interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetExpiredEphemeralServices(ctx, ttl, limit any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExpiredEphemeralServices", reflect.TypeOf((*MockStore)(nil).GetExpiredEphemeralServices), ctx, ttl, limit) } @@ -2006,7 +2012,7 @@ func (m *MockStore) GetGroupByID(ctx context.Context, lockStrength LockingStreng } // GetGroupByID indicates an expected call of GetGroupByID. -func (mr *MockStoreMockRecorder) GetGroupByID(ctx, lockStrength, accountID, groupID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetGroupByID(ctx, lockStrength, accountID, groupID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGroupByID", reflect.TypeOf((*MockStore)(nil).GetGroupByID), ctx, lockStrength, accountID, groupID) } @@ -2021,7 +2027,7 @@ func (m *MockStore) GetGroupByName(ctx context.Context, lockStrength LockingStre } // GetGroupByName indicates an expected call of GetGroupByName. -func (mr *MockStoreMockRecorder) GetGroupByName(ctx, lockStrength, accountID, groupName interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetGroupByName(ctx, lockStrength, accountID, groupName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGroupByName", reflect.TypeOf((*MockStore)(nil).GetGroupByName), ctx, lockStrength, accountID, groupName) } @@ -2036,7 +2042,7 @@ func (m *MockStore) GetGroupIDsByPeerIDs(ctx context.Context, accountID string, } // GetGroupIDsByPeerIDs indicates an expected call of GetGroupIDsByPeerIDs. -func (mr *MockStoreMockRecorder) GetGroupIDsByPeerIDs(ctx, accountID, peerIDs interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetGroupIDsByPeerIDs(ctx, accountID, peerIDs any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGroupIDsByPeerIDs", reflect.TypeOf((*MockStore)(nil).GetGroupIDsByPeerIDs), ctx, accountID, peerIDs) } @@ -2051,7 +2057,7 @@ func (m *MockStore) GetGroupsByIDs(ctx context.Context, lockStrength LockingStre } // GetGroupsByIDs indicates an expected call of GetGroupsByIDs. -func (mr *MockStoreMockRecorder) GetGroupsByIDs(ctx, lockStrength, accountID, groupIDs interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetGroupsByIDs(ctx, lockStrength, accountID, groupIDs any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGroupsByIDs", reflect.TypeOf((*MockStore)(nil).GetGroupsByIDs), ctx, lockStrength, accountID, groupIDs) } @@ -2080,7 +2086,7 @@ func (m *MockStore) GetNameServerGroupByID(ctx context.Context, lockStrength Loc } // GetNameServerGroupByID indicates an expected call of GetNameServerGroupByID. -func (mr *MockStoreMockRecorder) GetNameServerGroupByID(ctx, lockStrength, nameServerGroupID, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetNameServerGroupByID(ctx, lockStrength, nameServerGroupID, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNameServerGroupByID", reflect.TypeOf((*MockStore)(nil).GetNameServerGroupByID), ctx, lockStrength, nameServerGroupID, accountID) } @@ -2095,7 +2101,7 @@ func (m *MockStore) GetNetworkByID(ctx context.Context, lockStrength LockingStre } // GetNetworkByID indicates an expected call of GetNetworkByID. -func (mr *MockStoreMockRecorder) GetNetworkByID(ctx, lockStrength, accountID, networkID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetNetworkByID(ctx, lockStrength, accountID, networkID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetworkByID", reflect.TypeOf((*MockStore)(nil).GetNetworkByID), ctx, lockStrength, accountID, networkID) } @@ -2110,7 +2116,7 @@ func (m *MockStore) GetNetworkResourceByID(ctx context.Context, lockStrength Loc } // GetNetworkResourceByID indicates an expected call of GetNetworkResourceByID. -func (mr *MockStoreMockRecorder) GetNetworkResourceByID(ctx, lockStrength, accountID, resourceID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetNetworkResourceByID(ctx, lockStrength, accountID, resourceID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetworkResourceByID", reflect.TypeOf((*MockStore)(nil).GetNetworkResourceByID), ctx, lockStrength, accountID, resourceID) } @@ -2125,7 +2131,7 @@ func (m *MockStore) GetNetworkResourceByName(ctx context.Context, lockStrength L } // GetNetworkResourceByName indicates an expected call of GetNetworkResourceByName. -func (mr *MockStoreMockRecorder) GetNetworkResourceByName(ctx, lockStrength, accountID, resourceName interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetNetworkResourceByName(ctx, lockStrength, accountID, resourceName any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetworkResourceByName", reflect.TypeOf((*MockStore)(nil).GetNetworkResourceByName), ctx, lockStrength, accountID, resourceName) } @@ -2140,7 +2146,7 @@ func (m *MockStore) GetNetworkResourcesByAccountID(ctx context.Context, lockStre } // GetNetworkResourcesByAccountID indicates an expected call of GetNetworkResourcesByAccountID. -func (mr *MockStoreMockRecorder) GetNetworkResourcesByAccountID(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetNetworkResourcesByAccountID(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetworkResourcesByAccountID", reflect.TypeOf((*MockStore)(nil).GetNetworkResourcesByAccountID), ctx, lockStrength, accountID) } @@ -2155,7 +2161,7 @@ func (m *MockStore) GetNetworkResourcesByNetID(ctx context.Context, lockStrength } // GetNetworkResourcesByNetID indicates an expected call of GetNetworkResourcesByNetID. -func (mr *MockStoreMockRecorder) GetNetworkResourcesByNetID(ctx, lockStrength, accountID, netID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetNetworkResourcesByNetID(ctx, lockStrength, accountID, netID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetworkResourcesByNetID", reflect.TypeOf((*MockStore)(nil).GetNetworkResourcesByNetID), ctx, lockStrength, accountID, netID) } @@ -2170,7 +2176,7 @@ func (m *MockStore) GetNetworkRouterByID(ctx context.Context, lockStrength Locki } // GetNetworkRouterByID indicates an expected call of GetNetworkRouterByID. -func (mr *MockStoreMockRecorder) GetNetworkRouterByID(ctx, lockStrength, accountID, routerID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetNetworkRouterByID(ctx, lockStrength, accountID, routerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetworkRouterByID", reflect.TypeOf((*MockStore)(nil).GetNetworkRouterByID), ctx, lockStrength, accountID, routerID) } @@ -2185,7 +2191,7 @@ func (m *MockStore) GetNetworkRoutersByAccountID(ctx context.Context, lockStreng } // GetNetworkRoutersByAccountID indicates an expected call of GetNetworkRoutersByAccountID. -func (mr *MockStoreMockRecorder) GetNetworkRoutersByAccountID(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetNetworkRoutersByAccountID(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetworkRoutersByAccountID", reflect.TypeOf((*MockStore)(nil).GetNetworkRoutersByAccountID), ctx, lockStrength, accountID) } @@ -2200,7 +2206,7 @@ func (m *MockStore) GetNetworkRoutersByNetID(ctx context.Context, lockStrength L } // GetNetworkRoutersByNetID indicates an expected call of GetNetworkRoutersByNetID. -func (mr *MockStoreMockRecorder) GetNetworkRoutersByNetID(ctx, lockStrength, accountID, netID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetNetworkRoutersByNetID(ctx, lockStrength, accountID, netID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetworkRoutersByNetID", reflect.TypeOf((*MockStore)(nil).GetNetworkRoutersByNetID), ctx, lockStrength, accountID, netID) } @@ -2215,7 +2221,7 @@ func (m *MockStore) GetPATByHashedToken(ctx context.Context, lockStrength Lockin } // GetPATByHashedToken indicates an expected call of GetPATByHashedToken. -func (mr *MockStoreMockRecorder) GetPATByHashedToken(ctx, lockStrength, hashedToken interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetPATByHashedToken(ctx, lockStrength, hashedToken any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPATByHashedToken", reflect.TypeOf((*MockStore)(nil).GetPATByHashedToken), ctx, lockStrength, hashedToken) } @@ -2230,7 +2236,7 @@ func (m *MockStore) GetPATByID(ctx context.Context, lockStrength LockingStrength } // GetPATByID indicates an expected call of GetPATByID. -func (mr *MockStoreMockRecorder) GetPATByID(ctx, lockStrength, userID, patID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetPATByID(ctx, lockStrength, userID, patID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPATByID", reflect.TypeOf((*MockStore)(nil).GetPATByID), ctx, lockStrength, userID, patID) } @@ -2245,7 +2251,7 @@ func (m *MockStore) GetPeerByID(ctx context.Context, lockStrength LockingStrengt } // GetPeerByID indicates an expected call of GetPeerByID. -func (mr *MockStoreMockRecorder) GetPeerByID(ctx, lockStrength, accountID, peerID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetPeerByID(ctx, lockStrength, accountID, peerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerByID", reflect.TypeOf((*MockStore)(nil).GetPeerByID), ctx, lockStrength, accountID, peerID) } @@ -2260,7 +2266,7 @@ func (m *MockStore) GetPeerByIP(ctx context.Context, lockStrength LockingStrengt } // GetPeerByIP indicates an expected call of GetPeerByIP. -func (mr *MockStoreMockRecorder) GetPeerByIP(ctx, lockStrength, accountID, ip interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetPeerByIP(ctx, lockStrength, accountID, ip any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerByIP", reflect.TypeOf((*MockStore)(nil).GetPeerByIP), ctx, lockStrength, accountID, ip) } @@ -2275,7 +2281,7 @@ func (m *MockStore) GetPeerByPeerPubKey(ctx context.Context, lockStrength Lockin } // GetPeerByPeerPubKey indicates an expected call of GetPeerByPeerPubKey. -func (mr *MockStoreMockRecorder) GetPeerByPeerPubKey(ctx, lockStrength, peerKey interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetPeerByPeerPubKey(ctx, lockStrength, peerKey any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerByPeerPubKey", reflect.TypeOf((*MockStore)(nil).GetPeerByPeerPubKey), ctx, lockStrength, peerKey) } @@ -2290,7 +2296,7 @@ func (m *MockStore) GetPeerGroupIDs(ctx context.Context, lockStrength LockingStr } // GetPeerGroupIDs indicates an expected call of GetPeerGroupIDs. -func (mr *MockStoreMockRecorder) GetPeerGroupIDs(ctx, lockStrength, accountId, peerId interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetPeerGroupIDs(ctx, lockStrength, accountId, peerId any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerGroupIDs", reflect.TypeOf((*MockStore)(nil).GetPeerGroupIDs), ctx, lockStrength, accountId, peerId) } @@ -2305,7 +2311,7 @@ func (m *MockStore) GetPeerGroups(ctx context.Context, lockStrength LockingStren } // GetPeerGroups indicates an expected call of GetPeerGroups. -func (mr *MockStoreMockRecorder) GetPeerGroups(ctx, lockStrength, accountId, peerId interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetPeerGroups(ctx, lockStrength, accountId, peerId any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerGroups", reflect.TypeOf((*MockStore)(nil).GetPeerGroups), ctx, lockStrength, accountId, peerId) } @@ -2320,7 +2326,7 @@ func (m *MockStore) GetPeerIDByKey(ctx context.Context, lockStrength LockingStre } // GetPeerIDByKey indicates an expected call of GetPeerIDByKey. -func (mr *MockStoreMockRecorder) GetPeerIDByKey(ctx, lockStrength, key interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetPeerIDByKey(ctx, lockStrength, key any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerIDByKey", reflect.TypeOf((*MockStore)(nil).GetPeerIDByKey), ctx, lockStrength, key) } @@ -2335,7 +2341,7 @@ func (m *MockStore) GetPeerIDsByGroups(ctx context.Context, accountID string, gr } // GetPeerIDsByGroups indicates an expected call of GetPeerIDsByGroups. -func (mr *MockStoreMockRecorder) GetPeerIDsByGroups(ctx, accountID, groupIDs interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetPeerIDsByGroups(ctx, accountID, groupIDs any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerIDsByGroups", reflect.TypeOf((*MockStore)(nil).GetPeerIDsByGroups), ctx, accountID, groupIDs) } @@ -2350,7 +2356,7 @@ func (m *MockStore) GetPeerIdByLabel(ctx context.Context, lockStrength LockingSt } // GetPeerIdByLabel indicates an expected call of GetPeerIdByLabel. -func (mr *MockStoreMockRecorder) GetPeerIdByLabel(ctx, lockStrength, accountID, hostname interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetPeerIdByLabel(ctx, lockStrength, accountID, hostname any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerIdByLabel", reflect.TypeOf((*MockStore)(nil).GetPeerIdByLabel), ctx, lockStrength, accountID, hostname) } @@ -2365,7 +2371,7 @@ func (m *MockStore) GetPeerJobByID(ctx context.Context, accountID, jobID string) } // GetPeerJobByID indicates an expected call of GetPeerJobByID. -func (mr *MockStoreMockRecorder) GetPeerJobByID(ctx, accountID, jobID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetPeerJobByID(ctx, accountID, jobID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerJobByID", reflect.TypeOf((*MockStore)(nil).GetPeerJobByID), ctx, accountID, jobID) } @@ -2380,7 +2386,7 @@ func (m *MockStore) GetPeerJobs(ctx context.Context, accountID, peerID string) ( } // GetPeerJobs indicates an expected call of GetPeerJobs. -func (mr *MockStoreMockRecorder) GetPeerJobs(ctx, accountID, peerID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetPeerJobs(ctx, accountID, peerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerJobs", reflect.TypeOf((*MockStore)(nil).GetPeerJobs), ctx, accountID, peerID) } @@ -2395,7 +2401,7 @@ func (m *MockStore) GetPeerLabelsInAccount(ctx context.Context, lockStrength Loc } // GetPeerLabelsInAccount indicates an expected call of GetPeerLabelsInAccount. -func (mr *MockStoreMockRecorder) GetPeerLabelsInAccount(ctx, lockStrength, accountId, hostname interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetPeerLabelsInAccount(ctx, lockStrength, accountId, hostname any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerLabelsInAccount", reflect.TypeOf((*MockStore)(nil).GetPeerLabelsInAccount), ctx, lockStrength, accountId, hostname) } @@ -2410,7 +2416,7 @@ func (m *MockStore) GetPeersByGroupIDs(ctx context.Context, accountID string, gr } // GetPeersByGroupIDs indicates an expected call of GetPeersByGroupIDs. -func (mr *MockStoreMockRecorder) GetPeersByGroupIDs(ctx, accountID, groupIDs interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetPeersByGroupIDs(ctx, accountID, groupIDs any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeersByGroupIDs", reflect.TypeOf((*MockStore)(nil).GetPeersByGroupIDs), ctx, accountID, groupIDs) } @@ -2425,7 +2431,7 @@ func (m *MockStore) GetPeersByIDs(ctx context.Context, lockStrength LockingStren } // GetPeersByIDs indicates an expected call of GetPeersByIDs. -func (mr *MockStoreMockRecorder) GetPeersByIDs(ctx, lockStrength, accountID, peerIDs interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetPeersByIDs(ctx, lockStrength, accountID, peerIDs any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeersByIDs", reflect.TypeOf((*MockStore)(nil).GetPeersByIDs), ctx, lockStrength, accountID, peerIDs) } @@ -2440,7 +2446,7 @@ func (m *MockStore) GetPolicyByID(ctx context.Context, lockStrength LockingStren } // GetPolicyByID indicates an expected call of GetPolicyByID. -func (mr *MockStoreMockRecorder) GetPolicyByID(ctx, lockStrength, accountID, policyID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetPolicyByID(ctx, lockStrength, accountID, policyID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPolicyByID", reflect.TypeOf((*MockStore)(nil).GetPolicyByID), ctx, lockStrength, accountID, policyID) } @@ -2455,7 +2461,7 @@ func (m *MockStore) GetPolicyRulesByResourceID(ctx context.Context, lockStrength } // GetPolicyRulesByResourceID indicates an expected call of GetPolicyRulesByResourceID. -func (mr *MockStoreMockRecorder) GetPolicyRulesByResourceID(ctx, lockStrength, accountID, peerID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetPolicyRulesByResourceID(ctx, lockStrength, accountID, peerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPolicyRulesByResourceID", reflect.TypeOf((*MockStore)(nil).GetPolicyRulesByResourceID), ctx, lockStrength, accountID, peerID) } @@ -2470,7 +2476,7 @@ func (m *MockStore) GetPostureCheckByChecksDefinition(accountID string, checks * } // GetPostureCheckByChecksDefinition indicates an expected call of GetPostureCheckByChecksDefinition. -func (mr *MockStoreMockRecorder) GetPostureCheckByChecksDefinition(accountID, checks interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetPostureCheckByChecksDefinition(accountID, checks any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPostureCheckByChecksDefinition", reflect.TypeOf((*MockStore)(nil).GetPostureCheckByChecksDefinition), accountID, checks) } @@ -2485,7 +2491,7 @@ func (m *MockStore) GetPostureChecksByID(ctx context.Context, lockStrength Locki } // GetPostureChecksByID indicates an expected call of GetPostureChecksByID. -func (mr *MockStoreMockRecorder) GetPostureChecksByID(ctx, lockStrength, accountID, postureCheckID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetPostureChecksByID(ctx, lockStrength, accountID, postureCheckID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPostureChecksByID", reflect.TypeOf((*MockStore)(nil).GetPostureChecksByID), ctx, lockStrength, accountID, postureCheckID) } @@ -2500,7 +2506,7 @@ func (m *MockStore) GetPostureChecksByIDs(ctx context.Context, lockStrength Lock } // GetPostureChecksByIDs indicates an expected call of GetPostureChecksByIDs. -func (mr *MockStoreMockRecorder) GetPostureChecksByIDs(ctx, lockStrength, accountID, postureChecksIDs interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetPostureChecksByIDs(ctx, lockStrength, accountID, postureChecksIDs any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPostureChecksByIDs", reflect.TypeOf((*MockStore)(nil).GetPostureChecksByIDs), ctx, lockStrength, accountID, postureChecksIDs) } @@ -2515,7 +2521,7 @@ func (m *MockStore) GetProxyAccessTokenByHashedToken(ctx context.Context, lockSt } // GetProxyAccessTokenByHashedToken indicates an expected call of GetProxyAccessTokenByHashedToken. -func (mr *MockStoreMockRecorder) GetProxyAccessTokenByHashedToken(ctx, lockStrength, hashedToken interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetProxyAccessTokenByHashedToken(ctx, lockStrength, hashedToken any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProxyAccessTokenByHashedToken", reflect.TypeOf((*MockStore)(nil).GetProxyAccessTokenByHashedToken), ctx, lockStrength, hashedToken) } @@ -2530,7 +2536,7 @@ func (m *MockStore) GetProxyAccessTokenByID(ctx context.Context, lockStrength Lo } // GetProxyAccessTokenByID indicates an expected call of GetProxyAccessTokenByID. -func (mr *MockStoreMockRecorder) GetProxyAccessTokenByID(ctx, lockStrength, tokenID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetProxyAccessTokenByID(ctx, lockStrength, tokenID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProxyAccessTokenByID", reflect.TypeOf((*MockStore)(nil).GetProxyAccessTokenByID), ctx, lockStrength, tokenID) } @@ -2545,7 +2551,7 @@ func (m *MockStore) GetProxyAccessTokensByAccountID(ctx context.Context, lockStr } // GetProxyAccessTokensByAccountID indicates an expected call of GetProxyAccessTokensByAccountID. -func (mr *MockStoreMockRecorder) GetProxyAccessTokensByAccountID(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetProxyAccessTokensByAccountID(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProxyAccessTokensByAccountID", reflect.TypeOf((*MockStore)(nil).GetProxyAccessTokensByAccountID), ctx, lockStrength, accountID) } @@ -2560,7 +2566,7 @@ func (m *MockStore) GetProxyByAccountID(ctx context.Context, accountID string) ( } // GetProxyByAccountID indicates an expected call of GetProxyByAccountID. -func (mr *MockStoreMockRecorder) GetProxyByAccountID(ctx, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetProxyByAccountID(ctx, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProxyByAccountID", reflect.TypeOf((*MockStore)(nil).GetProxyByAccountID), ctx, accountID) } @@ -2575,7 +2581,7 @@ func (m *MockStore) GetProxyClusters(ctx context.Context, accountID string) ([]p } // GetProxyClusters indicates an expected call of GetProxyClusters. -func (mr *MockStoreMockRecorder) GetProxyClusters(ctx, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetProxyClusters(ctx, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProxyClusters", reflect.TypeOf((*MockStore)(nil).GetProxyClusters), ctx, accountID) } @@ -2590,7 +2596,7 @@ func (m *MockStore) GetProxyMetrics(ctx context.Context) (ProxyMetrics, error) { } // GetProxyMetrics indicates an expected call of GetProxyMetrics. -func (mr *MockStoreMockRecorder) GetProxyMetrics(ctx interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetProxyMetrics(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProxyMetrics", reflect.TypeOf((*MockStore)(nil).GetProxyMetrics), ctx) } @@ -2605,7 +2611,7 @@ func (m *MockStore) GetResourceGroups(ctx context.Context, lockStrength LockingS } // GetResourceGroups indicates an expected call of GetResourceGroups. -func (mr *MockStoreMockRecorder) GetResourceGroups(ctx, lockStrength, accountID, resourceID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetResourceGroups(ctx, lockStrength, accountID, resourceID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetResourceGroups", reflect.TypeOf((*MockStore)(nil).GetResourceGroups), ctx, lockStrength, accountID, resourceID) } @@ -2620,7 +2626,7 @@ func (m *MockStore) GetRouteByID(ctx context.Context, lockStrength LockingStreng } // GetRouteByID indicates an expected call of GetRouteByID. -func (mr *MockStoreMockRecorder) GetRouteByID(ctx, lockStrength, accountID, routeID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetRouteByID(ctx, lockStrength, accountID, routeID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRouteByID", reflect.TypeOf((*MockStore)(nil).GetRouteByID), ctx, lockStrength, accountID, routeID) } @@ -2635,24 +2641,24 @@ func (m *MockStore) GetRoutingPeerNetworks(ctx context.Context, accountID, peerI } // GetRoutingPeerNetworks indicates an expected call of GetRoutingPeerNetworks. -func (mr *MockStoreMockRecorder) GetRoutingPeerNetworks(ctx, accountID, peerID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetRoutingPeerNetworks(ctx, accountID, peerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRoutingPeerNetworks", reflect.TypeOf((*MockStore)(nil).GetRoutingPeerNetworks), ctx, accountID, peerID) } // GetServiceByDomain mocks base method. -func (m *MockStore) GetServiceByDomain(ctx context.Context, domain string) (*service.Service, error) { +func (m *MockStore) GetServiceByDomain(ctx context.Context, arg1 string) (*service.Service, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetServiceByDomain", ctx, domain) + ret := m.ctrl.Call(m, "GetServiceByDomain", ctx, arg1) ret0, _ := ret[0].(*service.Service) ret1, _ := ret[1].(error) return ret0, ret1 } // GetServiceByDomain indicates an expected call of GetServiceByDomain. -func (mr *MockStoreMockRecorder) GetServiceByDomain(ctx, domain interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetServiceByDomain(ctx, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceByDomain", reflect.TypeOf((*MockStore)(nil).GetServiceByDomain), ctx, domain) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceByDomain", reflect.TypeOf((*MockStore)(nil).GetServiceByDomain), ctx, arg1) } // GetServiceByID mocks base method. @@ -2665,7 +2671,7 @@ func (m *MockStore) GetServiceByID(ctx context.Context, lockStrength LockingStre } // GetServiceByID indicates an expected call of GetServiceByID. -func (mr *MockStoreMockRecorder) GetServiceByID(ctx, lockStrength, accountID, serviceID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetServiceByID(ctx, lockStrength, accountID, serviceID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceByID", reflect.TypeOf((*MockStore)(nil).GetServiceByID), ctx, lockStrength, accountID, serviceID) } @@ -2680,7 +2686,7 @@ func (m *MockStore) GetServiceTargetByTargetID(ctx context.Context, lockStrength } // GetServiceTargetByTargetID indicates an expected call of GetServiceTargetByTargetID. -func (mr *MockStoreMockRecorder) GetServiceTargetByTargetID(ctx, lockStrength, accountID, targetID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetServiceTargetByTargetID(ctx, lockStrength, accountID, targetID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceTargetByTargetID", reflect.TypeOf((*MockStore)(nil).GetServiceTargetByTargetID), ctx, lockStrength, accountID, targetID) } @@ -2695,7 +2701,7 @@ func (m *MockStore) GetServices(ctx context.Context, lockStrength LockingStrengt } // GetServices indicates an expected call of GetServices. -func (mr *MockStoreMockRecorder) GetServices(ctx, lockStrength interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetServices(ctx, lockStrength any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServices", reflect.TypeOf((*MockStore)(nil).GetServices), ctx, lockStrength) } @@ -2710,7 +2716,7 @@ func (m *MockStore) GetServicesByCluster(ctx context.Context, lockStrength Locki } // GetServicesByCluster indicates an expected call of GetServicesByCluster. -func (mr *MockStoreMockRecorder) GetServicesByCluster(ctx, lockStrength, proxyCluster interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetServicesByCluster(ctx, lockStrength, proxyCluster any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServicesByCluster", reflect.TypeOf((*MockStore)(nil).GetServicesByCluster), ctx, lockStrength, proxyCluster) } @@ -2725,7 +2731,7 @@ func (m *MockStore) GetServicesByClusterAndPort(ctx context.Context, lockStrengt } // GetServicesByClusterAndPort indicates an expected call of GetServicesByClusterAndPort. -func (mr *MockStoreMockRecorder) GetServicesByClusterAndPort(ctx, lockStrength, proxyCluster, mode, listenPort interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetServicesByClusterAndPort(ctx, lockStrength, proxyCluster, mode, listenPort any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServicesByClusterAndPort", reflect.TypeOf((*MockStore)(nil).GetServicesByClusterAndPort), ctx, lockStrength, proxyCluster, mode, listenPort) } @@ -2740,7 +2746,7 @@ func (m *MockStore) GetSetupKeyByID(ctx context.Context, lockStrength LockingStr } // GetSetupKeyByID indicates an expected call of GetSetupKeyByID. -func (mr *MockStoreMockRecorder) GetSetupKeyByID(ctx, lockStrength, accountID, setupKeyID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetSetupKeyByID(ctx, lockStrength, accountID, setupKeyID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSetupKeyByID", reflect.TypeOf((*MockStore)(nil).GetSetupKeyByID), ctx, lockStrength, accountID, setupKeyID) } @@ -2755,7 +2761,7 @@ func (m *MockStore) GetSetupKeyBySecret(ctx context.Context, lockStrength Lockin } // GetSetupKeyBySecret indicates an expected call of GetSetupKeyBySecret. -func (mr *MockStoreMockRecorder) GetSetupKeyBySecret(ctx, lockStrength, key interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetSetupKeyBySecret(ctx, lockStrength, key any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSetupKeyBySecret", reflect.TypeOf((*MockStore)(nil).GetSetupKeyBySecret), ctx, lockStrength, key) } @@ -2784,7 +2790,7 @@ func (m *MockStore) GetTakenIPs(ctx context.Context, lockStrength LockingStrengt } // GetTakenIPs indicates an expected call of GetTakenIPs. -func (mr *MockStoreMockRecorder) GetTakenIPs(ctx, lockStrength, accountId interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetTakenIPs(ctx, lockStrength, accountId any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTakenIPs", reflect.TypeOf((*MockStore)(nil).GetTakenIPs), ctx, lockStrength, accountId) } @@ -2799,7 +2805,7 @@ func (m *MockStore) GetTargetsByServiceID(ctx context.Context, lockStrength Lock } // GetTargetsByServiceID indicates an expected call of GetTargetsByServiceID. -func (mr *MockStoreMockRecorder) GetTargetsByServiceID(ctx, lockStrength, accountID, serviceID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetTargetsByServiceID(ctx, lockStrength, accountID, serviceID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTargetsByServiceID", reflect.TypeOf((*MockStore)(nil).GetTargetsByServiceID), ctx, lockStrength, accountID, serviceID) } @@ -2814,7 +2820,7 @@ func (m *MockStore) GetTokenIDByHashedToken(ctx context.Context, secret string) } // GetTokenIDByHashedToken indicates an expected call of GetTokenIDByHashedToken. -func (mr *MockStoreMockRecorder) GetTokenIDByHashedToken(ctx, secret interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetTokenIDByHashedToken(ctx, secret any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTokenIDByHashedToken", reflect.TypeOf((*MockStore)(nil).GetTokenIDByHashedToken), ctx, secret) } @@ -2829,7 +2835,7 @@ func (m *MockStore) GetUserByPATID(ctx context.Context, lockStrength LockingStre } // GetUserByPATID indicates an expected call of GetUserByPATID. -func (mr *MockStoreMockRecorder) GetUserByPATID(ctx, lockStrength, patID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetUserByPATID(ctx, lockStrength, patID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserByPATID", reflect.TypeOf((*MockStore)(nil).GetUserByPATID), ctx, lockStrength, patID) } @@ -2844,7 +2850,7 @@ func (m *MockStore) GetUserByUserID(ctx context.Context, lockStrength LockingStr } // GetUserByUserID indicates an expected call of GetUserByUserID. -func (mr *MockStoreMockRecorder) GetUserByUserID(ctx, lockStrength, userID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetUserByUserID(ctx, lockStrength, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserByUserID", reflect.TypeOf((*MockStore)(nil).GetUserByUserID), ctx, lockStrength, userID) } @@ -2859,7 +2865,7 @@ func (m *MockStore) GetUserIDByPeerKey(ctx context.Context, lockStrength Locking } // GetUserIDByPeerKey indicates an expected call of GetUserIDByPeerKey. -func (mr *MockStoreMockRecorder) GetUserIDByPeerKey(ctx, lockStrength, peerKey interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetUserIDByPeerKey(ctx, lockStrength, peerKey any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserIDByPeerKey", reflect.TypeOf((*MockStore)(nil).GetUserIDByPeerKey), ctx, lockStrength, peerKey) } @@ -2874,7 +2880,7 @@ func (m *MockStore) GetUserInviteByEmail(ctx context.Context, lockStrength Locki } // GetUserInviteByEmail indicates an expected call of GetUserInviteByEmail. -func (mr *MockStoreMockRecorder) GetUserInviteByEmail(ctx, lockStrength, accountID, email interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetUserInviteByEmail(ctx, lockStrength, accountID, email any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserInviteByEmail", reflect.TypeOf((*MockStore)(nil).GetUserInviteByEmail), ctx, lockStrength, accountID, email) } @@ -2889,7 +2895,7 @@ func (m *MockStore) GetUserInviteByHashedToken(ctx context.Context, lockStrength } // GetUserInviteByHashedToken indicates an expected call of GetUserInviteByHashedToken. -func (mr *MockStoreMockRecorder) GetUserInviteByHashedToken(ctx, lockStrength, hashedToken interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetUserInviteByHashedToken(ctx, lockStrength, hashedToken any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserInviteByHashedToken", reflect.TypeOf((*MockStore)(nil).GetUserInviteByHashedToken), ctx, lockStrength, hashedToken) } @@ -2904,7 +2910,7 @@ func (m *MockStore) GetUserInviteByID(ctx context.Context, lockStrength LockingS } // GetUserInviteByID indicates an expected call of GetUserInviteByID. -func (mr *MockStoreMockRecorder) GetUserInviteByID(ctx, lockStrength, accountID, inviteID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetUserInviteByID(ctx, lockStrength, accountID, inviteID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserInviteByID", reflect.TypeOf((*MockStore)(nil).GetUserInviteByID), ctx, lockStrength, accountID, inviteID) } @@ -2919,7 +2925,7 @@ func (m *MockStore) GetUserPATs(ctx context.Context, lockStrength LockingStrengt } // GetUserPATs indicates an expected call of GetUserPATs. -func (mr *MockStoreMockRecorder) GetUserPATs(ctx, lockStrength, userID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetUserPATs(ctx, lockStrength, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserPATs", reflect.TypeOf((*MockStore)(nil).GetUserPATs), ctx, lockStrength, userID) } @@ -2934,24 +2940,24 @@ func (m *MockStore) GetUserPeers(ctx context.Context, lockStrength LockingStreng } // GetUserPeers indicates an expected call of GetUserPeers. -func (mr *MockStoreMockRecorder) GetUserPeers(ctx, lockStrength, accountID, userID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetUserPeers(ctx, lockStrength, accountID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserPeers", reflect.TypeOf((*MockStore)(nil).GetUserPeers), ctx, lockStrength, accountID, userID) } // GetZoneByDomain mocks base method. -func (m *MockStore) GetZoneByDomain(ctx context.Context, accountID, domain string) (*zones.Zone, error) { +func (m *MockStore) GetZoneByDomain(ctx context.Context, accountID, arg2 string) (*zones.Zone, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetZoneByDomain", ctx, accountID, domain) + ret := m.ctrl.Call(m, "GetZoneByDomain", ctx, accountID, arg2) ret0, _ := ret[0].(*zones.Zone) ret1, _ := ret[1].(error) return ret0, ret1 } // GetZoneByDomain indicates an expected call of GetZoneByDomain. -func (mr *MockStoreMockRecorder) GetZoneByDomain(ctx, accountID, domain interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetZoneByDomain(ctx, accountID, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetZoneByDomain", reflect.TypeOf((*MockStore)(nil).GetZoneByDomain), ctx, accountID, domain) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetZoneByDomain", reflect.TypeOf((*MockStore)(nil).GetZoneByDomain), ctx, accountID, arg2) } // GetZoneByID mocks base method. @@ -2964,7 +2970,7 @@ func (m *MockStore) GetZoneByID(ctx context.Context, lockStrength LockingStrengt } // GetZoneByID indicates an expected call of GetZoneByID. -func (mr *MockStoreMockRecorder) GetZoneByID(ctx, lockStrength, accountID, zoneID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetZoneByID(ctx, lockStrength, accountID, zoneID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetZoneByID", reflect.TypeOf((*MockStore)(nil).GetZoneByID), ctx, lockStrength, accountID, zoneID) } @@ -2979,7 +2985,7 @@ func (m *MockStore) GetZoneDNSRecords(ctx context.Context, lockStrength LockingS } // GetZoneDNSRecords indicates an expected call of GetZoneDNSRecords. -func (mr *MockStoreMockRecorder) GetZoneDNSRecords(ctx, lockStrength, accountID, zoneID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetZoneDNSRecords(ctx, lockStrength, accountID, zoneID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetZoneDNSRecords", reflect.TypeOf((*MockStore)(nil).GetZoneDNSRecords), ctx, lockStrength, accountID, zoneID) } @@ -2994,7 +3000,7 @@ func (m *MockStore) GetZoneDNSRecordsByName(ctx context.Context, lockStrength Lo } // GetZoneDNSRecordsByName indicates an expected call of GetZoneDNSRecordsByName. -func (mr *MockStoreMockRecorder) GetZoneDNSRecordsByName(ctx, lockStrength, accountID, zoneID, name interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) GetZoneDNSRecordsByName(ctx, lockStrength, accountID, zoneID, name any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetZoneDNSRecordsByName", reflect.TypeOf((*MockStore)(nil).GetZoneDNSRecordsByName), ctx, lockStrength, accountID, zoneID, name) } @@ -3009,7 +3015,7 @@ func (m *MockStore) HasActiveProxyAtClusterAddress(ctx context.Context, clusterA } // HasActiveProxyAtClusterAddress indicates an expected call of HasActiveProxyAtClusterAddress. -func (mr *MockStoreMockRecorder) HasActiveProxyAtClusterAddress(ctx, clusterAddress interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) HasActiveProxyAtClusterAddress(ctx, clusterAddress any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasActiveProxyAtClusterAddress", reflect.TypeOf((*MockStore)(nil).HasActiveProxyAtClusterAddress), ctx, clusterAddress) } @@ -3023,7 +3029,7 @@ func (m *MockStore) IncrementAgentNetworkConsumption(ctx context.Context, accoun } // IncrementAgentNetworkConsumption indicates an expected call of IncrementAgentNetworkConsumption. -func (mr *MockStoreMockRecorder) IncrementAgentNetworkConsumption(ctx, accountID, kind, dimID, windowSeconds, windowStart, tokensIn, tokensOut, costUSD interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) IncrementAgentNetworkConsumption(ctx, accountID, kind, dimID, windowSeconds, windowStart, tokensIn, tokensOut, costUSD any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IncrementAgentNetworkConsumption", reflect.TypeOf((*MockStore)(nil).IncrementAgentNetworkConsumption), ctx, accountID, kind, dimID, windowSeconds, windowStart, tokensIn, tokensOut, costUSD) } @@ -3037,7 +3043,7 @@ func (m *MockStore) IncrementAgentNetworkConsumptionBatch(ctx context.Context, a } // IncrementAgentNetworkConsumptionBatch indicates an expected call of IncrementAgentNetworkConsumptionBatch. -func (mr *MockStoreMockRecorder) IncrementAgentNetworkConsumptionBatch(ctx, accountID, keys, tokensIn, tokensOut, costUSD interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) IncrementAgentNetworkConsumptionBatch(ctx, accountID, keys, tokensIn, tokensOut, costUSD any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IncrementAgentNetworkConsumptionBatch", reflect.TypeOf((*MockStore)(nil).IncrementAgentNetworkConsumptionBatch), ctx, accountID, keys, tokensIn, tokensOut, costUSD) } @@ -3051,7 +3057,7 @@ func (m *MockStore) IncrementNetworkSerial(ctx context.Context, accountId string } // IncrementNetworkSerial indicates an expected call of IncrementNetworkSerial. -func (mr *MockStoreMockRecorder) IncrementNetworkSerial(ctx, accountId interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) IncrementNetworkSerial(ctx, accountId any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IncrementNetworkSerial", reflect.TypeOf((*MockStore)(nil).IncrementNetworkSerial), ctx, accountId) } @@ -3065,7 +3071,7 @@ func (m *MockStore) IncrementSetupKeyUsage(ctx context.Context, setupKeyID strin } // IncrementSetupKeyUsage indicates an expected call of IncrementSetupKeyUsage. -func (mr *MockStoreMockRecorder) IncrementSetupKeyUsage(ctx, setupKeyID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) IncrementSetupKeyUsage(ctx, setupKeyID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IncrementSetupKeyUsage", reflect.TypeOf((*MockStore)(nil).IncrementSetupKeyUsage), ctx, setupKeyID) } @@ -3080,7 +3086,7 @@ func (m *MockStore) IsClusterAddressConflicting(ctx context.Context, clusterAddr } // IsClusterAddressConflicting indicates an expected call of IsClusterAddressConflicting. -func (mr *MockStoreMockRecorder) IsClusterAddressConflicting(ctx, clusterAddress, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) IsClusterAddressConflicting(ctx, clusterAddress, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsClusterAddressConflicting", reflect.TypeOf((*MockStore)(nil).IsClusterAddressConflicting), ctx, clusterAddress, accountID) } @@ -3096,7 +3102,7 @@ func (m *MockStore) IsPrimaryAccount(ctx context.Context, accountID string) (boo } // IsPrimaryAccount indicates an expected call of IsPrimaryAccount. -func (mr *MockStoreMockRecorder) IsPrimaryAccount(ctx, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) IsPrimaryAccount(ctx, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsPrimaryAccount", reflect.TypeOf((*MockStore)(nil).IsPrimaryAccount), ctx, accountID) } @@ -3111,7 +3117,7 @@ func (m *MockStore) IsProxyAccessTokenValid(ctx context.Context, tokenID string) } // IsProxyAccessTokenValid indicates an expected call of IsProxyAccessTokenValid. -func (mr *MockStoreMockRecorder) IsProxyAccessTokenValid(ctx, tokenID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) IsProxyAccessTokenValid(ctx, tokenID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsProxyAccessTokenValid", reflect.TypeOf((*MockStore)(nil).IsProxyAccessTokenValid), ctx, tokenID) } @@ -3126,7 +3132,7 @@ func (m *MockStore) ListAgentNetworkConsumption(ctx context.Context, lockStrengt } // ListAgentNetworkConsumption indicates an expected call of ListAgentNetworkConsumption. -func (mr *MockStoreMockRecorder) ListAgentNetworkConsumption(ctx, lockStrength, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) ListAgentNetworkConsumption(ctx, lockStrength, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAgentNetworkConsumption", reflect.TypeOf((*MockStore)(nil).ListAgentNetworkConsumption), ctx, lockStrength, accountID) } @@ -3141,7 +3147,7 @@ func (m *MockStore) ListCustomDomains(ctx context.Context, accountID string) ([] } // ListCustomDomains indicates an expected call of ListCustomDomains. -func (mr *MockStoreMockRecorder) ListCustomDomains(ctx, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) ListCustomDomains(ctx, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListCustomDomains", reflect.TypeOf((*MockStore)(nil).ListCustomDomains), ctx, accountID) } @@ -3156,7 +3162,7 @@ func (m *MockStore) ListFreeDomains(ctx context.Context, accountID string) ([]st } // ListFreeDomains indicates an expected call of ListFreeDomains. -func (mr *MockStoreMockRecorder) ListFreeDomains(ctx, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) ListFreeDomains(ctx, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListFreeDomains", reflect.TypeOf((*MockStore)(nil).ListFreeDomains), ctx, accountID) } @@ -3170,7 +3176,7 @@ func (m *MockStore) MarkAccountPrimary(ctx context.Context, accountID string) er } // MarkAccountPrimary indicates an expected call of MarkAccountPrimary. -func (mr *MockStoreMockRecorder) MarkAccountPrimary(ctx, accountID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) MarkAccountPrimary(ctx, accountID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MarkAccountPrimary", reflect.TypeOf((*MockStore)(nil).MarkAccountPrimary), ctx, accountID) } @@ -3184,7 +3190,7 @@ func (m *MockStore) MarkAllPendingJobsAsFailed(ctx context.Context, accountID, p } // MarkAllPendingJobsAsFailed indicates an expected call of MarkAllPendingJobsAsFailed. -func (mr *MockStoreMockRecorder) MarkAllPendingJobsAsFailed(ctx, accountID, peerID, reason interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) MarkAllPendingJobsAsFailed(ctx, accountID, peerID, reason any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MarkAllPendingJobsAsFailed", reflect.TypeOf((*MockStore)(nil).MarkAllPendingJobsAsFailed), ctx, accountID, peerID, reason) } @@ -3198,7 +3204,7 @@ func (m *MockStore) MarkPATUsed(ctx context.Context, patID string) error { } // MarkPATUsed indicates an expected call of MarkPATUsed. -func (mr *MockStoreMockRecorder) MarkPATUsed(ctx, patID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) MarkPATUsed(ctx, patID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MarkPATUsed", reflect.TypeOf((*MockStore)(nil).MarkPATUsed), ctx, patID) } @@ -3213,7 +3219,7 @@ func (m *MockStore) MarkPeerConnectedIfNewerSession(ctx context.Context, account } // MarkPeerConnectedIfNewerSession indicates an expected call of MarkPeerConnectedIfNewerSession. -func (mr *MockStoreMockRecorder) MarkPeerConnectedIfNewerSession(ctx, accountID, peerID, newSessionStartedAt interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) MarkPeerConnectedIfNewerSession(ctx, accountID, peerID, newSessionStartedAt any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MarkPeerConnectedIfNewerSession", reflect.TypeOf((*MockStore)(nil).MarkPeerConnectedIfNewerSession), ctx, accountID, peerID, newSessionStartedAt) } @@ -3228,7 +3234,7 @@ func (m *MockStore) MarkPeerDisconnectedIfSameSession(ctx context.Context, accou } // MarkPeerDisconnectedIfSameSession indicates an expected call of MarkPeerDisconnectedIfSameSession. -func (mr *MockStoreMockRecorder) MarkPeerDisconnectedIfSameSession(ctx, accountID, peerID, sessionStartedAt interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) MarkPeerDisconnectedIfSameSession(ctx, accountID, peerID, sessionStartedAt any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MarkPeerDisconnectedIfSameSession", reflect.TypeOf((*MockStore)(nil).MarkPeerDisconnectedIfSameSession), ctx, accountID, peerID, sessionStartedAt) } @@ -3242,7 +3248,7 @@ func (m *MockStore) MarkPendingJobsAsFailed(ctx context.Context, accountID, peer } // MarkPendingJobsAsFailed indicates an expected call of MarkPendingJobsAsFailed. -func (mr *MockStoreMockRecorder) MarkPendingJobsAsFailed(ctx, accountID, peerID, jobID, reason interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) MarkPendingJobsAsFailed(ctx, accountID, peerID, jobID, reason any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MarkPendingJobsAsFailed", reflect.TypeOf((*MockStore)(nil).MarkPendingJobsAsFailed), ctx, accountID, peerID, jobID, reason) } @@ -3256,7 +3262,7 @@ func (m *MockStore) MarkProxyAccessTokenUsed(ctx context.Context, tokenID string } // MarkProxyAccessTokenUsed indicates an expected call of MarkProxyAccessTokenUsed. -func (mr *MockStoreMockRecorder) MarkProxyAccessTokenUsed(ctx, tokenID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) MarkProxyAccessTokenUsed(ctx, tokenID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MarkProxyAccessTokenUsed", reflect.TypeOf((*MockStore)(nil).MarkProxyAccessTokenUsed), ctx, tokenID) } @@ -3271,7 +3277,7 @@ func (m *MockStore) RefreshPeerLastSeen(ctx context.Context, accountID, peerID s } // RefreshPeerLastSeen indicates an expected call of RefreshPeerLastSeen. -func (mr *MockStoreMockRecorder) RefreshPeerLastSeen(ctx, accountID, peerID, staleBefore interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) RefreshPeerLastSeen(ctx, accountID, peerID, staleBefore any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshPeerLastSeen", reflect.TypeOf((*MockStore)(nil).RefreshPeerLastSeen), ctx, accountID, peerID, staleBefore) } @@ -3285,7 +3291,7 @@ func (m *MockStore) RemovePeerFromAllGroups(ctx context.Context, peerID string) } // RemovePeerFromAllGroups indicates an expected call of RemovePeerFromAllGroups. -func (mr *MockStoreMockRecorder) RemovePeerFromAllGroups(ctx, peerID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) RemovePeerFromAllGroups(ctx, peerID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemovePeerFromAllGroups", reflect.TypeOf((*MockStore)(nil).RemovePeerFromAllGroups), ctx, peerID) } @@ -3299,7 +3305,7 @@ func (m *MockStore) RemovePeerFromGroup(ctx context.Context, peerID, groupID str } // RemovePeerFromGroup indicates an expected call of RemovePeerFromGroup. -func (mr *MockStoreMockRecorder) RemovePeerFromGroup(ctx, peerID, groupID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) RemovePeerFromGroup(ctx, peerID, groupID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemovePeerFromGroup", reflect.TypeOf((*MockStore)(nil).RemovePeerFromGroup), ctx, peerID, groupID) } @@ -3313,7 +3319,7 @@ func (m *MockStore) RemoveResourceFromGroup(ctx context.Context, accountId, grou } // RemoveResourceFromGroup indicates an expected call of RemoveResourceFromGroup. -func (mr *MockStoreMockRecorder) RemoveResourceFromGroup(ctx, accountId, groupID, resourceID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) RemoveResourceFromGroup(ctx, accountId, groupID, resourceID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveResourceFromGroup", reflect.TypeOf((*MockStore)(nil).RemoveResourceFromGroup), ctx, accountId, groupID, resourceID) } @@ -3327,7 +3333,7 @@ func (m *MockStore) RenewEphemeralService(ctx context.Context, accountID, peerID } // RenewEphemeralService indicates an expected call of RenewEphemeralService. -func (mr *MockStoreMockRecorder) RenewEphemeralService(ctx, accountID, peerID, serviceID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) RenewEphemeralService(ctx, accountID, peerID, serviceID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RenewEphemeralService", reflect.TypeOf((*MockStore)(nil).RenewEphemeralService), ctx, accountID, peerID, serviceID) } @@ -3341,7 +3347,7 @@ func (m *MockStore) RevokeProxyAccessToken(ctx context.Context, tokenID string) } // RevokeProxyAccessToken indicates an expected call of RevokeProxyAccessToken. -func (mr *MockStoreMockRecorder) RevokeProxyAccessToken(ctx, tokenID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) RevokeProxyAccessToken(ctx, tokenID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RevokeProxyAccessToken", reflect.TypeOf((*MockStore)(nil).RevokeProxyAccessToken), ctx, tokenID) } @@ -3355,7 +3361,7 @@ func (m *MockStore) SaveAccount(ctx context.Context, account *types3.Account) er } // SaveAccount indicates an expected call of SaveAccount. -func (mr *MockStoreMockRecorder) SaveAccount(ctx, account interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SaveAccount(ctx, account any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveAccount", reflect.TypeOf((*MockStore)(nil).SaveAccount), ctx, account) } @@ -3369,7 +3375,7 @@ func (m *MockStore) SaveAccountOnboarding(ctx context.Context, onboarding *types } // SaveAccountOnboarding indicates an expected call of SaveAccountOnboarding. -func (mr *MockStoreMockRecorder) SaveAccountOnboarding(ctx, onboarding interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SaveAccountOnboarding(ctx, onboarding any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveAccountOnboarding", reflect.TypeOf((*MockStore)(nil).SaveAccountOnboarding), ctx, onboarding) } @@ -3383,7 +3389,7 @@ func (m *MockStore) SaveAccountSettings(ctx context.Context, accountID string, s } // SaveAccountSettings indicates an expected call of SaveAccountSettings. -func (mr *MockStoreMockRecorder) SaveAccountSettings(ctx, accountID, settings interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SaveAccountSettings(ctx, accountID, settings any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveAccountSettings", reflect.TypeOf((*MockStore)(nil).SaveAccountSettings), ctx, accountID, settings) } @@ -3397,7 +3403,7 @@ func (m *MockStore) SaveAgentNetworkBudgetRule(ctx context.Context, rule *types. } // SaveAgentNetworkBudgetRule indicates an expected call of SaveAgentNetworkBudgetRule. -func (mr *MockStoreMockRecorder) SaveAgentNetworkBudgetRule(ctx, rule interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SaveAgentNetworkBudgetRule(ctx, rule any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveAgentNetworkBudgetRule", reflect.TypeOf((*MockStore)(nil).SaveAgentNetworkBudgetRule), ctx, rule) } @@ -3411,7 +3417,7 @@ func (m *MockStore) SaveAgentNetworkGuardrail(ctx context.Context, guardrail *ty } // SaveAgentNetworkGuardrail indicates an expected call of SaveAgentNetworkGuardrail. -func (mr *MockStoreMockRecorder) SaveAgentNetworkGuardrail(ctx, guardrail interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SaveAgentNetworkGuardrail(ctx, guardrail any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveAgentNetworkGuardrail", reflect.TypeOf((*MockStore)(nil).SaveAgentNetworkGuardrail), ctx, guardrail) } @@ -3425,7 +3431,7 @@ func (m *MockStore) SaveAgentNetworkPolicy(ctx context.Context, policy *types.Po } // SaveAgentNetworkPolicy indicates an expected call of SaveAgentNetworkPolicy. -func (mr *MockStoreMockRecorder) SaveAgentNetworkPolicy(ctx, policy interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SaveAgentNetworkPolicy(ctx, policy any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveAgentNetworkPolicy", reflect.TypeOf((*MockStore)(nil).SaveAgentNetworkPolicy), ctx, policy) } @@ -3439,7 +3445,7 @@ func (m *MockStore) SaveAgentNetworkProvider(ctx context.Context, provider *type } // SaveAgentNetworkProvider indicates an expected call of SaveAgentNetworkProvider. -func (mr *MockStoreMockRecorder) SaveAgentNetworkProvider(ctx, provider interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SaveAgentNetworkProvider(ctx, provider any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveAgentNetworkProvider", reflect.TypeOf((*MockStore)(nil).SaveAgentNetworkProvider), ctx, provider) } @@ -3453,7 +3459,7 @@ func (m *MockStore) SaveAgentNetworkSettings(ctx context.Context, settings *type } // SaveAgentNetworkSettings indicates an expected call of SaveAgentNetworkSettings. -func (mr *MockStoreMockRecorder) SaveAgentNetworkSettings(ctx, settings interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SaveAgentNetworkSettings(ctx, settings any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveAgentNetworkSettings", reflect.TypeOf((*MockStore)(nil).SaveAgentNetworkSettings), ctx, settings) } @@ -3467,7 +3473,7 @@ func (m *MockStore) SaveDNSSettings(ctx context.Context, accountID string, setti } // SaveDNSSettings indicates an expected call of SaveDNSSettings. -func (mr *MockStoreMockRecorder) SaveDNSSettings(ctx, accountID, settings interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SaveDNSSettings(ctx, accountID, settings any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveDNSSettings", reflect.TypeOf((*MockStore)(nil).SaveDNSSettings), ctx, accountID, settings) } @@ -3481,7 +3487,7 @@ func (m *MockStore) SaveInstallationID(ctx context.Context, ID string) error { } // SaveInstallationID indicates an expected call of SaveInstallationID. -func (mr *MockStoreMockRecorder) SaveInstallationID(ctx, ID interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SaveInstallationID(ctx, ID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveInstallationID", reflect.TypeOf((*MockStore)(nil).SaveInstallationID), ctx, ID) } @@ -3495,7 +3501,7 @@ func (m *MockStore) SaveNameServerGroup(ctx context.Context, nameServerGroup *dn } // SaveNameServerGroup indicates an expected call of SaveNameServerGroup. -func (mr *MockStoreMockRecorder) SaveNameServerGroup(ctx, nameServerGroup interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SaveNameServerGroup(ctx, nameServerGroup any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveNameServerGroup", reflect.TypeOf((*MockStore)(nil).SaveNameServerGroup), ctx, nameServerGroup) } @@ -3509,7 +3515,7 @@ func (m *MockStore) SaveNetwork(ctx context.Context, network *types2.Network) er } // SaveNetwork indicates an expected call of SaveNetwork. -func (mr *MockStoreMockRecorder) SaveNetwork(ctx, network interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SaveNetwork(ctx, network any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveNetwork", reflect.TypeOf((*MockStore)(nil).SaveNetwork), ctx, network) } @@ -3523,7 +3529,7 @@ func (m *MockStore) SaveNetworkResource(ctx context.Context, resource *types0.Ne } // SaveNetworkResource indicates an expected call of SaveNetworkResource. -func (mr *MockStoreMockRecorder) SaveNetworkResource(ctx, resource interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SaveNetworkResource(ctx, resource any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveNetworkResource", reflect.TypeOf((*MockStore)(nil).SaveNetworkResource), ctx, resource) } @@ -3537,23 +3543,23 @@ func (m *MockStore) SavePAT(ctx context.Context, pat *types3.PersonalAccessToken } // SavePAT indicates an expected call of SavePAT. -func (mr *MockStoreMockRecorder) SavePAT(ctx, pat interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SavePAT(ctx, pat any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SavePAT", reflect.TypeOf((*MockStore)(nil).SavePAT), ctx, pat) } // SavePeer mocks base method. -func (m *MockStore) SavePeer(ctx context.Context, accountID string, peer *peer.Peer) error { +func (m *MockStore) SavePeer(ctx context.Context, accountID string, arg2 *peer.Peer) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SavePeer", ctx, accountID, peer) + ret := m.ctrl.Call(m, "SavePeer", ctx, accountID, arg2) ret0, _ := ret[0].(error) return ret0 } // SavePeer indicates an expected call of SavePeer. -func (mr *MockStoreMockRecorder) SavePeer(ctx, accountID, peer interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SavePeer(ctx, accountID, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SavePeer", reflect.TypeOf((*MockStore)(nil).SavePeer), ctx, accountID, peer) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SavePeer", reflect.TypeOf((*MockStore)(nil).SavePeer), ctx, accountID, arg2) } // SavePeerStatus mocks base method. @@ -3565,7 +3571,7 @@ func (m *MockStore) SavePeerStatus(ctx context.Context, accountID, peerID string } // SavePeerStatus indicates an expected call of SavePeerStatus. -func (mr *MockStoreMockRecorder) SavePeerStatus(ctx, accountID, peerID, status interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SavePeerStatus(ctx, accountID, peerID, status any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SavePeerStatus", reflect.TypeOf((*MockStore)(nil).SavePeerStatus), ctx, accountID, peerID, status) } @@ -3579,7 +3585,7 @@ func (m *MockStore) SavePolicy(ctx context.Context, policy *types3.Policy) error } // SavePolicy indicates an expected call of SavePolicy. -func (mr *MockStoreMockRecorder) SavePolicy(ctx, policy interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SavePolicy(ctx, policy any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SavePolicy", reflect.TypeOf((*MockStore)(nil).SavePolicy), ctx, policy) } @@ -3593,23 +3599,23 @@ func (m *MockStore) SavePostureChecks(ctx context.Context, postureCheck *posture } // SavePostureChecks indicates an expected call of SavePostureChecks. -func (mr *MockStoreMockRecorder) SavePostureChecks(ctx, postureCheck interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SavePostureChecks(ctx, postureCheck any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SavePostureChecks", reflect.TypeOf((*MockStore)(nil).SavePostureChecks), ctx, postureCheck) } // SaveProxy mocks base method. -func (m *MockStore) SaveProxy(ctx context.Context, proxy *proxy.Proxy) error { +func (m *MockStore) SaveProxy(ctx context.Context, arg1 *proxy.Proxy) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SaveProxy", ctx, proxy) + ret := m.ctrl.Call(m, "SaveProxy", ctx, arg1) ret0, _ := ret[0].(error) return ret0 } // SaveProxy indicates an expected call of SaveProxy. -func (mr *MockStoreMockRecorder) SaveProxy(ctx, proxy interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SaveProxy(ctx, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveProxy", reflect.TypeOf((*MockStore)(nil).SaveProxy), ctx, proxy) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveProxy", reflect.TypeOf((*MockStore)(nil).SaveProxy), ctx, arg1) } // SaveProxyAccessToken mocks base method. @@ -3621,23 +3627,23 @@ func (m *MockStore) SaveProxyAccessToken(ctx context.Context, token *types3.Prox } // SaveProxyAccessToken indicates an expected call of SaveProxyAccessToken. -func (mr *MockStoreMockRecorder) SaveProxyAccessToken(ctx, token interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SaveProxyAccessToken(ctx, token any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveProxyAccessToken", reflect.TypeOf((*MockStore)(nil).SaveProxyAccessToken), ctx, token) } // SaveRoute mocks base method. -func (m *MockStore) SaveRoute(ctx context.Context, route *route.Route) error { +func (m *MockStore) SaveRoute(ctx context.Context, arg1 *route.Route) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SaveRoute", ctx, route) + ret := m.ctrl.Call(m, "SaveRoute", ctx, arg1) ret0, _ := ret[0].(error) return ret0 } // SaveRoute indicates an expected call of SaveRoute. -func (mr *MockStoreMockRecorder) SaveRoute(ctx, route interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SaveRoute(ctx, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveRoute", reflect.TypeOf((*MockStore)(nil).SaveRoute), ctx, route) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveRoute", reflect.TypeOf((*MockStore)(nil).SaveRoute), ctx, arg1) } // SaveSetupKey mocks base method. @@ -3649,7 +3655,7 @@ func (m *MockStore) SaveSetupKey(ctx context.Context, setupKey *types3.SetupKey) } // SaveSetupKey indicates an expected call of SaveSetupKey. -func (mr *MockStoreMockRecorder) SaveSetupKey(ctx, setupKey interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SaveSetupKey(ctx, setupKey any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveSetupKey", reflect.TypeOf((*MockStore)(nil).SaveSetupKey), ctx, setupKey) } @@ -3663,7 +3669,7 @@ func (m *MockStore) SaveUser(ctx context.Context, user *types3.User) error { } // SaveUser indicates an expected call of SaveUser. -func (mr *MockStoreMockRecorder) SaveUser(ctx, user interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SaveUser(ctx, user any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveUser", reflect.TypeOf((*MockStore)(nil).SaveUser), ctx, user) } @@ -3677,7 +3683,7 @@ func (m *MockStore) SaveUserInvite(ctx context.Context, invite *types3.UserInvit } // SaveUserInvite indicates an expected call of SaveUserInvite. -func (mr *MockStoreMockRecorder) SaveUserInvite(ctx, invite interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SaveUserInvite(ctx, invite any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveUserInvite", reflect.TypeOf((*MockStore)(nil).SaveUserInvite), ctx, invite) } @@ -3691,7 +3697,7 @@ func (m *MockStore) SaveUserLastLogin(ctx context.Context, accountID, userID str } // SaveUserLastLogin indicates an expected call of SaveUserLastLogin. -func (mr *MockStoreMockRecorder) SaveUserLastLogin(ctx, accountID, userID, lastLogin interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SaveUserLastLogin(ctx, accountID, userID, lastLogin any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveUserLastLogin", reflect.TypeOf((*MockStore)(nil).SaveUserLastLogin), ctx, accountID, userID, lastLogin) } @@ -3705,7 +3711,7 @@ func (m *MockStore) SaveUsers(ctx context.Context, users []*types3.User) error { } // SaveUsers indicates an expected call of SaveUsers. -func (mr *MockStoreMockRecorder) SaveUsers(ctx, users interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SaveUsers(ctx, users any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveUsers", reflect.TypeOf((*MockStore)(nil).SaveUsers), ctx, users) } @@ -3717,23 +3723,23 @@ func (m *MockStore) SetFieldEncrypt(enc *crypt.FieldEncrypt) { } // SetFieldEncrypt indicates an expected call of SetFieldEncrypt. -func (mr *MockStoreMockRecorder) SetFieldEncrypt(enc interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) SetFieldEncrypt(enc any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetFieldEncrypt", reflect.TypeOf((*MockStore)(nil).SetFieldEncrypt), enc) } // UpdateAccountDomainAttributes mocks base method. -func (m *MockStore) UpdateAccountDomainAttributes(ctx context.Context, accountID, domain, category string, isPrimaryDomain bool) error { +func (m *MockStore) UpdateAccountDomainAttributes(ctx context.Context, accountID, arg2, category string, isPrimaryDomain bool) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateAccountDomainAttributes", ctx, accountID, domain, category, isPrimaryDomain) + ret := m.ctrl.Call(m, "UpdateAccountDomainAttributes", ctx, accountID, arg2, category, isPrimaryDomain) ret0, _ := ret[0].(error) return ret0 } // UpdateAccountDomainAttributes indicates an expected call of UpdateAccountDomainAttributes. -func (mr *MockStoreMockRecorder) UpdateAccountDomainAttributes(ctx, accountID, domain, category, isPrimaryDomain interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) UpdateAccountDomainAttributes(ctx, accountID, arg2, category, isPrimaryDomain any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAccountDomainAttributes", reflect.TypeOf((*MockStore)(nil).UpdateAccountDomainAttributes), ctx, accountID, domain, category, isPrimaryDomain) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAccountDomainAttributes", reflect.TypeOf((*MockStore)(nil).UpdateAccountDomainAttributes), ctx, accountID, arg2, category, isPrimaryDomain) } // UpdateAccountNetwork mocks base method. @@ -3745,7 +3751,7 @@ func (m *MockStore) UpdateAccountNetwork(ctx context.Context, accountID string, } // UpdateAccountNetwork indicates an expected call of UpdateAccountNetwork. -func (mr *MockStoreMockRecorder) UpdateAccountNetwork(ctx, accountID, ipNet interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) UpdateAccountNetwork(ctx, accountID, ipNet any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAccountNetwork", reflect.TypeOf((*MockStore)(nil).UpdateAccountNetwork), ctx, accountID, ipNet) } @@ -3759,7 +3765,7 @@ func (m *MockStore) UpdateAccountNetworkV6(ctx context.Context, accountID string } // UpdateAccountNetworkV6 indicates an expected call of UpdateAccountNetworkV6. -func (mr *MockStoreMockRecorder) UpdateAccountNetworkV6(ctx, accountID, ipNet interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) UpdateAccountNetworkV6(ctx, accountID, ipNet any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAccountNetworkV6", reflect.TypeOf((*MockStore)(nil).UpdateAccountNetworkV6), ctx, accountID, ipNet) } @@ -3774,7 +3780,7 @@ func (m *MockStore) UpdateCustomDomain(ctx context.Context, accountID string, d } // UpdateCustomDomain indicates an expected call of UpdateCustomDomain. -func (mr *MockStoreMockRecorder) UpdateCustomDomain(ctx, accountID, d interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) UpdateCustomDomain(ctx, accountID, d any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateCustomDomain", reflect.TypeOf((*MockStore)(nil).UpdateCustomDomain), ctx, accountID, d) } @@ -3788,7 +3794,7 @@ func (m *MockStore) UpdateDNSRecord(ctx context.Context, record *records.Record) } // UpdateDNSRecord indicates an expected call of UpdateDNSRecord. -func (mr *MockStoreMockRecorder) UpdateDNSRecord(ctx, record interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) UpdateDNSRecord(ctx, record any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDNSRecord", reflect.TypeOf((*MockStore)(nil).UpdateDNSRecord), ctx, record) } @@ -3802,7 +3808,7 @@ func (m *MockStore) UpdateGroup(ctx context.Context, group *types3.Group) error } // UpdateGroup indicates an expected call of UpdateGroup. -func (mr *MockStoreMockRecorder) UpdateGroup(ctx, group interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) UpdateGroup(ctx, group any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateGroup", reflect.TypeOf((*MockStore)(nil).UpdateGroup), ctx, group) } @@ -3816,7 +3822,7 @@ func (m *MockStore) UpdateGroups(ctx context.Context, accountID string, groups [ } // UpdateGroups indicates an expected call of UpdateGroups. -func (mr *MockStoreMockRecorder) UpdateGroups(ctx, accountID, groups interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) UpdateGroups(ctx, accountID, groups any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateGroups", reflect.TypeOf((*MockStore)(nil).UpdateGroups), ctx, accountID, groups) } @@ -3830,7 +3836,7 @@ func (m *MockStore) UpdateNetworkRouter(ctx context.Context, router *types1.Netw } // UpdateNetworkRouter indicates an expected call of UpdateNetworkRouter. -func (mr *MockStoreMockRecorder) UpdateNetworkRouter(ctx, router interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) UpdateNetworkRouter(ctx, router any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateNetworkRouter", reflect.TypeOf((*MockStore)(nil).UpdateNetworkRouter), ctx, router) } @@ -3844,23 +3850,23 @@ func (m *MockStore) UpdateProxyHeartbeat(ctx context.Context, p *proxy.Proxy) er } // UpdateProxyHeartbeat indicates an expected call of UpdateProxyHeartbeat. -func (mr *MockStoreMockRecorder) UpdateProxyHeartbeat(ctx, p interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) UpdateProxyHeartbeat(ctx, p any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateProxyHeartbeat", reflect.TypeOf((*MockStore)(nil).UpdateProxyHeartbeat), ctx, p) } // UpdateService mocks base method. -func (m *MockStore) UpdateService(ctx context.Context, service *service.Service) error { +func (m *MockStore) UpdateService(ctx context.Context, arg1 *service.Service) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateService", ctx, service) + ret := m.ctrl.Call(m, "UpdateService", ctx, arg1) ret0, _ := ret[0].(error) return ret0 } // UpdateService indicates an expected call of UpdateService. -func (mr *MockStoreMockRecorder) UpdateService(ctx, service interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) UpdateService(ctx, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateService", reflect.TypeOf((*MockStore)(nil).UpdateService), ctx, service) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateService", reflect.TypeOf((*MockStore)(nil).UpdateService), ctx, arg1) } // UpdateZone mocks base method. @@ -3872,7 +3878,7 @@ func (m *MockStore) UpdateZone(ctx context.Context, zone *zones.Zone) error { } // UpdateZone indicates an expected call of UpdateZone. -func (mr *MockStoreMockRecorder) UpdateZone(ctx, zone interface{}) *gomock.Call { +func (mr *MockStoreMockRecorder) UpdateZone(ctx, zone any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateZone", reflect.TypeOf((*MockStore)(nil).UpdateZone), ctx, zone) } diff --git a/shared/management/client/client_test.go b/shared/management/client/client_test.go index 570de7631..d4888fee2 100644 --- a/shared/management/client/client_test.go +++ b/shared/management/client/client_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -591,7 +591,7 @@ func Test_GetPKCEAuthorizationFlow(t *testing.T) { expectedFlowInfo := &mgmtProto.PKCEAuthorizationFlow{ ProviderConfig: &mgmtProto.ProviderConfig{ ClientID: "client", - ClientSecret: "secret", + ClientSecret: "secret", //nolint:staticcheck }, } diff --git a/shared/management/client/grpc.go b/shared/management/client/grpc.go index 81f25900a..cd250b5f7 100644 --- a/shared/management/client/grpc.go +++ b/shared/management/client/grpc.go @@ -21,6 +21,8 @@ import ( "google.golang.org/grpc/connectivity" nbgrpc "github.com/netbirdio/netbird/client/grpc" + "github.com/netbirdio/netbird/client/netstate" + "github.com/netbirdio/netbird/client/netsweep" "github.com/netbirdio/netbird/client/system" "github.com/netbirdio/netbird/encryption" "github.com/netbirdio/netbird/shared/management/domain" @@ -62,6 +64,13 @@ type GrpcClient struct { connStateCallbackLock sync.RWMutex serverURL string + // netState gates the stream retry loop on OS-reported network + // availability; nil (the default) disables gating. + netState *netstate.State + + // sweeper cuts the transport connections on network change; nil disables it. + sweeper *netsweep.Sweeper + // syncStreamErr holds the last Sync stream error, or nil while the stream // is established and healthy. GetServerKey succeeds even when the peer // cannot sync (e.g. the server returns "settings not found"), so the @@ -111,16 +120,43 @@ func MaxRecvMsgSize() int { return size } +// Option configures optional GrpcClient behavior. +type Option func(*GrpcClient) + +// WithNetworkState injects the OS network availability state that gates the +// stream retry loop; without it gating is disabled. +func WithNetworkState(netState *netstate.State) Option { + return func(c *GrpcClient) { c.netState = netState } +} + +// WithSweeper injects the network change sweeper. +func WithSweeper(sweeper *netsweep.Sweeper) Option { + return func(c *GrpcClient) { c.sweeper = sweeper } +} + // NewClient creates a new client to Management service -func NewClient(ctx context.Context, addr string, ourPrivateKey wgtypes.Key, tlsEnabled bool) (*GrpcClient, error) { - var conn *grpc.ClientConn +func NewClient(ctx context.Context, addr string, ourPrivateKey wgtypes.Key, tlsEnabled bool, opts ...Option) (*GrpcClient, error) { + // Options apply before dialing: the sweeper must wrap the first connection too. + c := &GrpcClient{ + key: ourPrivateKey, + ctx: ctx, + connStateCallbackLock: sync.RWMutex{}, + serverURL: addr, + } + for _, opt := range opts { + opt(c) + } var extraOpts []grpc.DialOption if maxSize := MaxRecvMsgSize(); maxSize > 0 { extraOpts = append(extraOpts, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxSize))) log.Infof("management gRPC max receive message size set to %d bytes", maxSize) } + if c.sweeper != nil { + extraOpts = append(extraOpts, nbgrpc.WithSweeper(c.sweeper)) + } + var conn *grpc.ClientConn operation := func() error { var err error conn, err = nbgrpc.CreateConnection(ctx, addr, tlsEnabled, wsproxy.ManagementComponent, extraOpts...) @@ -136,16 +172,9 @@ func NewClient(ctx context.Context, addr string, ourPrivateKey wgtypes.Key, tlsE return nil, err } - realClient := proto.NewManagementServiceClient(conn) - - return &GrpcClient{ - key: ourPrivateKey, - realClient: realClient, - ctx: ctx, - conn: conn, - connStateCallbackLock: sync.RWMutex{}, - serverURL: addr, - }, nil + c.conn = conn + c.realClient = proto.NewManagementServiceClient(conn) + return c, nil } // GetServerURL returns the management server URL @@ -206,16 +235,33 @@ func (c *GrpcClient) withMgmtStream( ctx context.Context, handler func(ctx context.Context, serverPubKey wgtypes.Key, backOff backoff.BackOff) error, ) error { - backOff := defaultBackoff(ctx) + backOff := c.sweeper.QuickRetryBackoff(ctx, defaultBackoff(ctx), c.netState) operation := func() error { - log.Debugf("management connection state %v", c.conn.GetState()) - connState := c.conn.GetState() + // suspend reconnect attempts while the OS reports no usable network. + // Wait only errors on a cancelled context, which means shutdown, so + // stop the loop without reporting a failure. + if waited, err := c.netState.Wait(ctx); err != nil { + log.Debugf("management connection context has been canceled while offline, this usually indicates shutdown") + return nil //nolint:nilerr // a cancelled context means shutdown, not a retryable failure + } else if waited { + backOff.Reset() + } + connState := c.conn.GetState() + log.Debugf("management connection state %v", connState) if connState == connectivity.Shutdown { return backoff.Permanent(fmt.Errorf("connection to management has been shut down")) - } else if !(connState == connectivity.Ready || connState == connectivity.Idle) { + } + if !(connState == connectivity.Ready || connState == connectivity.Idle) { + // A dial may already be in flight (e.g. the other stream triggered + // it after a network change); wait for it to settle and proceed if + // the channel became usable, instead of burning a backoff round on + // a successful dial. A failed dial errors out as before. c.conn.WaitForStateChange(ctx, connState) - return fmt.Errorf("connection to management is not ready and in %s state", connState) + connState = c.conn.GetState() + if !(connState == connectivity.Ready || connState == connectivity.Idle) { + return fmt.Errorf("connection to management is not ready and in %s state", connState) + } } serverPubKey, err := c.getServerPublicKey() @@ -227,7 +273,7 @@ func (c *GrpcClient) withMgmtStream( return handler(ctx, *serverPubKey, backOff) } - err := backoff.Retry(operation, backOff) + err := nbgrpc.Retry(ctx, operation, backOff, c.netState) if err != nil { log.Warnf("exiting the Management service connection retry loop due to the unrecoverable error: %s", err) } diff --git a/shared/management/networkmap/encode.go b/shared/management/networkmap/encode.go index ccde32faf..7e68861dc 100644 --- a/shared/management/networkmap/encode.go +++ b/shared/management/networkmap/encode.go @@ -247,7 +247,7 @@ func ToProtocolDNSConfig(update nbdns.Config, cache DNSConfigCache, forwardPort ServiceEnable: update.ServiceEnable, CustomZones: make([]*proto.CustomZone, 0, len(update.CustomZones)), NameServerGroups: make([]*proto.NameServerGroup, 0, len(update.NameServerGroups)), - ForwarderPort: forwardPort, + ForwarderPort: forwardPort, //nolint:staticcheck } for _, zone := range update.CustomZones { diff --git a/shared/relay/client/client.go b/shared/relay/client/client.go index 8d4aa6020..4fb30b8d9 100644 --- a/shared/relay/client/client.go +++ b/shared/relay/client/client.go @@ -14,6 +14,7 @@ import ( log "github.com/sirupsen/logrus" + "github.com/netbirdio/netbird/client/netsweep" auth "github.com/netbirdio/netbird/shared/relay/auth/hmac" "github.com/netbirdio/netbird/shared/relay/client/dialer" netErr "github.com/netbirdio/netbird/shared/relay/client/dialer/net" @@ -184,6 +185,10 @@ type Client struct { // datagram-sized transport is avoided on subsequent connects. Shared via // the manager. transportFallback *transportFallback + + // sweeper cuts the relay connection on network change; the read loop + // reports the disconnect and the guard reconnects. Shared via the manager. + sweeper *netsweep.Sweeper // datagramFallbackTriggered guards a single fallback per connection so a // burst of oversized datagrams triggers one reconnect, not many. datagramFallbackTriggered atomic.Bool @@ -393,6 +398,12 @@ func (c *Client) Close() error { } func (c *Client) connect(ctx context.Context) (*RelayAddr, error) { + // A sweep cancels this context, so a dial started on the old network + // aborts instead of waiting out its handshake timeout. + dial := c.sweeper.StartDial(ctx) + defer dial.Release() + ctx = dial.Ctx() + mode := transportModeFromEnv() dialers := c.getDialers(mode) @@ -417,12 +428,19 @@ func (c *Client) connect(ctx context.Context) (*RelayAddr, error) { return nil, fmt.Errorf("dial via FQDN: %w", err) } } - c.relayConn = conn - c.datagramFallbackTriggered.Store(false) + // Read the transport off the concrete connection: the sweeper's wrapper + // embeds net.Conn only, so it does not promote Protocol(). if tc, ok := conn.(transportConn); ok { c.transport = tc.Protocol() } + conn, err := dial.WrapConn(conn) + if err != nil { + return nil, fmt.Errorf("register connection: %w", err) + } + c.relayConn = conn + c.datagramFallbackTriggered.Store(false) + instanceURL, err := c.handShake(ctx) if err != nil { cErr := conn.Close() diff --git a/shared/relay/client/guard.go b/shared/relay/client/guard.go index d18534d9d..a62f8772d 100644 --- a/shared/relay/client/guard.go +++ b/shared/relay/client/guard.go @@ -7,9 +7,22 @@ import ( "github.com/cenkalti/backoff/v4" log "github.com/sirupsen/logrus" + + "github.com/netbirdio/netbird/client/netstate" ) -const defaultMaxBackoffInterval = 60 * time.Second +const ( + defaultMaxBackoffInterval = 60 * time.Second + + // quickReconnectBudget bounds how long a quick reconnect waits for the + // network before handing the retry over to the ticker. + quickReconnectBudget = 1500 * time.Millisecond + + // verdictSettleWindow is how long an online verdict must hold before it + // is trusted: the disconnect often precedes the OS offline flag by a few + // milliseconds. + verdictSettleWindow = 200 * time.Millisecond +) // Guard manage the reconnection tries to the Relay server in case of disconnection event. type Guard struct { @@ -22,14 +35,19 @@ type Guard struct { // attempts. maxBackoffInterval time.Duration + // netState gates reconnect attempts on OS-reported network availability; + // nil disables gating. + netState *netstate.State + // lastErr is the error from the most recent failed reconnect attempt, // surfaced as the home relay status while disconnected. lastErr atomic.Pointer[error] } // NewGuard creates a new guard for the relay client. A non-positive -// maxBackoffInterval falls back to defaultMaxBackoffInterval. -func NewGuard(sp *ServerPicker, maxBackoffInterval time.Duration) *Guard { +// maxBackoffInterval falls back to defaultMaxBackoffInterval. A nil netState +// disables network availability gating. +func NewGuard(sp *ServerPicker, maxBackoffInterval time.Duration, netState *netstate.State) *Guard { if maxBackoffInterval <= 0 { maxBackoffInterval = defaultMaxBackoffInterval } @@ -38,6 +56,7 @@ func NewGuard(sp *ServerPicker, maxBackoffInterval time.Duration) *Guard { OnReconnected: make(chan struct{}, 1), serverPicker: sp, maxBackoffInterval: maxBackoffInterval, + netState: netState, } return g } @@ -70,11 +89,21 @@ func (g *Guard) StartReconnectTrys(ctx context.Context, relayClient *Client) { // start a ticker to pick a new server ticker := g.exponentTicker(ctx) - defer ticker.Stop() + defer func() { + ticker.Stop() + }() for { select { case <-ticker.C: + // suspend reconnect attempts while the OS reports no usable network + if waited, err := g.netState.Wait(ctx); err != nil { + return + } else if waited { + ticker.Stop() + ticker = g.exponentTicker(ctx) + continue + } if err := g.retry(ctx); err != nil { log.Errorf("failed to pick new Relay server: %s", err) g.setLastError(err) @@ -100,7 +129,12 @@ func (g *Guard) tryToQuickReconnect(parentCtx context.Context, rc *Client) bool return false } - if cancelled := waiteBeforeRetry(parentCtx); !cancelled { + if ok := g.waitForNetwork(parentCtx); !ok { + return false + } + + // Still offline after the budget: leave the retry to the ticker. + if !g.netState.IsOnline() { return false } @@ -166,14 +200,47 @@ func (g *Guard) exponentTicker(ctx context.Context) *backoff.Ticker { return backoff.NewTicker(bo) } -func waiteBeforeRetry(ctx context.Context) bool { - timer := time.NewTimer(1500 * time.Millisecond) - defer timer.Stop() +// waitForNetwork waits out the settle window while online, or waits for the +// network to return while offline, within the budget. Returns false when ctx +// is cancelled. Without an injected netState it degrades to a fixed +// budget-long sleep, the pre-netstate behavior. +func (g *Guard) waitForNetwork(ctx context.Context) bool { + budget := time.NewTimer(quickReconnectBudget) + defer budget.Stop() - select { - case <-timer.C: - return true - case <-ctx.Done(): - return false + settleWindow := verdictSettleWindow + if g.netState == nil { + settleWindow = quickReconnectBudget + } + settle := time.NewTimer(settleWindow) + defer settle.Stop() + + for { + // Channel first, flag second: a flip in between still fires the channel. + changedCh := g.netState.Changed() + if g.netState.IsOnline() { + select { + case <-settle.C: + return true + case <-changedCh: + case <-ctx.Done(): + return false + } + } else { + select { + case <-budget.C: + return true + case <-changedCh: + case <-ctx.Done(): + return false + } + } + if !settle.Stop() { + select { + case <-settle.C: + default: + } + } + settle.Reset(settleWindow) } } diff --git a/shared/relay/client/guard_test.go b/shared/relay/client/guard_test.go new file mode 100644 index 000000000..0e05783e0 --- /dev/null +++ b/shared/relay/client/guard_test.go @@ -0,0 +1,30 @@ +package client + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/netbirdio/netbird/client/netstate" +) + +func TestWaitForNetworkSettlesAfterOutage(t *testing.T) { + ns := netstate.New() + ns.Set(false) + g := NewGuard(nil, 0, ns) + + const outage = 2 * verdictSettleWindow + start := time.Now() + go func() { + time.Sleep(outage) + ns.Set(true) + }() + + ok := g.waitForNetwork(context.Background()) + elapsed := time.Since(start) + + assert.True(t, ok, "recovered network must let the quick reconnect proceed") + assert.GreaterOrEqual(t, elapsed, outage+verdictSettleWindow, "reconnect must wait a full settle window after the network returns") +} diff --git a/shared/relay/client/manager.go b/shared/relay/client/manager.go index 2f2839d94..80e38ae2d 100644 --- a/shared/relay/client/manager.go +++ b/shared/relay/client/manager.go @@ -12,6 +12,8 @@ import ( log "github.com/sirupsen/logrus" + "github.com/netbirdio/netbird/client/netstate" + "github.com/netbirdio/netbird/client/netsweep" relayAuth "github.com/netbirdio/netbird/shared/relay/auth/hmac" ) @@ -65,6 +67,17 @@ func WithMaxBackoffInterval(d time.Duration) ManagerOption { return func(m *Manager) { m.maxBackoffInterval = d } } +// WithNetworkState injects the OS network availability state that gates the +// reconnect guard; without it reconnect attempts are not gated. +func WithNetworkState(netState *netstate.State) ManagerOption { + return func(m *Manager) { m.netState = netState } +} + +// WithSweeper injects the network change sweeper. +func WithSweeper(sweeper *netsweep.Sweeper) ManagerOption { + return func(m *Manager) { m.sweeper = sweeper } +} + // Manager is a manager for the relay client instances. It establishes one persistent connection to the given relay URL // and automatically reconnect to them in case disconnection. // The manager also manage temporary relay connection. If a client wants to communicate with a client on a @@ -92,6 +105,8 @@ type Manager struct { mtu uint16 maxBackoffInterval time.Duration + netState *netstate.State + sweeper *netsweep.Sweeper cleanupInterval time.Duration keepUnusedServerTime time.Duration @@ -128,8 +143,9 @@ func NewManager(ctx context.Context, serverURLs []string, peerID string, mtu uin for _, opt := range opts { opt(m) } + m.serverPicker.Sweeper = m.sweeper m.serverPicker.ServerURLs.Store(serverURLs) - m.reconnectGuard = NewGuard(m.serverPicker, m.maxBackoffInterval) + m.reconnectGuard = NewGuard(m.serverPicker, m.maxBackoffInterval, m.netState) return m } @@ -354,6 +370,7 @@ func (m *Manager) openConnVia(ctx context.Context, serverAddress, peerKey string relayClient := NewClientWithServerIP(serverAddress, serverIP, m.tokenStore, m.peerID, m.mtu) relayClient.SetTransportFallback(m.transportFallback) + relayClient.sweeper = m.sweeper err := relayClient.Connect(m.ctx) if err != nil { rt.Lock() diff --git a/shared/relay/client/picker.go b/shared/relay/client/picker.go index bb721e4ad..72789fadc 100644 --- a/shared/relay/client/picker.go +++ b/shared/relay/client/picker.go @@ -9,6 +9,7 @@ import ( log "github.com/sirupsen/logrus" + "github.com/netbirdio/netbird/client/netsweep" auth "github.com/netbirdio/netbird/shared/relay/auth/hmac" ) @@ -30,6 +31,7 @@ type ServerPicker struct { MTU uint16 ConnectionTimeout time.Duration TransportFallback *transportFallback + Sweeper *netsweep.Sweeper } func (sp *ServerPicker) PickServer(parentCtx context.Context) (*Client, error) { @@ -73,6 +75,7 @@ func (sp *ServerPicker) startConnection(ctx context.Context, resultChan chan con log.Infof("try to connecting to relay server: %s", url) relayClient := NewClient(url, sp.TokenStore, sp.PeerID, sp.MTU) relayClient.SetTransportFallback(sp.TransportFallback) + relayClient.sweeper = sp.Sweeper err := relayClient.Connect(ctx) resultChan <- connResult{ RelayClient: relayClient, diff --git a/shared/signal/client/grpc.go b/shared/signal/client/grpc.go index a07867263..73c482e8f 100644 --- a/shared/signal/client/grpc.go +++ b/shared/signal/client/grpc.go @@ -19,6 +19,8 @@ import ( "google.golang.org/grpc/status" nbgrpc "github.com/netbirdio/netbird/client/grpc" + "github.com/netbirdio/netbird/client/netstate" + "github.com/netbirdio/netbird/client/netsweep" "github.com/netbirdio/netbird/encryption" "github.com/netbirdio/netbird/shared/management/client" "github.com/netbirdio/netbird/shared/signal/proto" @@ -65,6 +67,13 @@ type GrpcClient struct { connStateCallback ConnStateNotifier connStateCallbackLock sync.RWMutex + // netState gates the Receive retry loop on OS-reported network + // availability; nil (the default) disables gating. + netState *netstate.State + + // sweeper cuts the transport connections on network change; nil disables it. + sweeper *netsweep.Sweeper + onReconnectedListenerFn func() decryptionWorker *Worker @@ -88,13 +97,43 @@ type GrpcClient struct { watchdogWg sync.WaitGroup } -// NewClient creates a new Signal client -func NewClient(ctx context.Context, addr string, key wgtypes.Key, tlsEnabled bool) (*GrpcClient, error) { - var conn *grpc.ClientConn +// Option configures optional GrpcClient behavior. +type Option func(*GrpcClient) +// WithNetworkState injects the OS network availability state that gates the +// Receive retry loop; without it gating is disabled. +func WithNetworkState(netState *netstate.State) Option { + return func(c *GrpcClient) { c.netState = netState } +} + +// WithSweeper injects the network change sweeper. +func WithSweeper(sweeper *netsweep.Sweeper) Option { + return func(c *GrpcClient) { c.sweeper = sweeper } +} + +// NewClient creates a new Signal client +func NewClient(ctx context.Context, addr string, key wgtypes.Key, tlsEnabled bool, opts ...Option) (*GrpcClient, error) { + // Options apply before dialing: the sweeper must wrap the first connection too. + c := &GrpcClient{ + ctx: ctx, + key: key, + mux: sync.Mutex{}, + status: StreamDisconnected, + connStateCallbackLock: sync.RWMutex{}, + } + for _, opt := range opts { + opt(c) + } + + var extraOpts []grpc.DialOption + if c.sweeper != nil { + extraOpts = append(extraOpts, nbgrpc.WithSweeper(c.sweeper)) + } + + var conn *grpc.ClientConn operation := func() error { var err error - conn, err = nbgrpc.CreateConnection(ctx, addr, tlsEnabled, wsproxy.SignalComponent) + conn, err = nbgrpc.CreateConnection(ctx, addr, tlsEnabled, wsproxy.SignalComponent, extraOpts...) if err != nil { return fmt.Errorf("create connection: %w", err) } @@ -109,15 +148,9 @@ func NewClient(ctx context.Context, addr string, key wgtypes.Key, tlsEnabled boo log.Debugf("connected to Signal Service: %v", conn.Target()) - return &GrpcClient{ - realClient: proto.NewSignalExchangeClient(conn), - ctx: ctx, - signalConn: conn, - key: key, - mux: sync.Mutex{}, - status: StreamDisconnected, - connStateCallbackLock: sync.RWMutex{}, - }, nil + c.signalConn = conn + c.realClient = proto.NewSignalExchangeClient(conn) + return c, nil } func (c *GrpcClient) StreamConnected() bool { @@ -165,19 +198,36 @@ func defaultBackoff(ctx context.Context) backoff.BackOff { // The connection retry logic will try to reconnect for 30 min and if wasn't successful will propagate the error to the function caller. func (c *GrpcClient) Receive(ctx context.Context, msgHandler func(msg *proto.Message) error) error { - var backOff = defaultBackoff(ctx) + backOff := c.sweeper.QuickRetryBackoff(ctx, defaultBackoff(ctx), c.netState) operation := func() error { + // suspend reconnect attempts while the OS reports no usable network. + // Wait only errors on a cancelled context, which means shutdown, so + // stop the loop without reporting a failure. + if waited, err := c.netState.Wait(ctx); err != nil { + log.Debugf("signal connection context has been canceled while offline, this usually indicates shutdown") + return nil + } else if waited { + backOff.Reset() + } c.notifyStreamDisconnected() - log.Debugf("signal connection state %v", c.signalConn.GetState()) connState := c.signalConn.GetState() + log.Debugf("signal connection state %v", connState) if connState == connectivity.Shutdown { return backoff.Permanent(fmt.Errorf("connection to signal has been shut down")) - } else if !(connState == connectivity.Ready || connState == connectivity.Idle) { + } + if !(connState == connectivity.Ready || connState == connectivity.Idle) { + // A dial may already be in flight (e.g. triggered by another RPC + // after a network change); wait for it to settle and proceed if + // the channel became usable, instead of burning a backoff round on + // a successful dial. A failed dial errors out as before. c.signalConn.WaitForStateChange(ctx, connState) - return fmt.Errorf("connection to signal is not ready and in %s state", connState) + connState = c.signalConn.GetState() + if !(connState == connectivity.Ready || connState == connectivity.Idle) { + return fmt.Errorf("connection to signal is not ready and in %s state", connState) + } } // connect to Signal stream identifying ourselves with a public WireGuard key @@ -231,7 +281,7 @@ func (c *GrpcClient) Receive(ctx context.Context, msgHandler func(msg *proto.Mes return nil } - err := backoff.Retry(operation, backOff) + err := nbgrpc.Retry(ctx, operation, backOff, c.netState) if err != nil { log.Errorf("exiting the Signal service connection retry loop due to the unrecoverable error: %v", err) return err diff --git a/sharedsock/example/main.go b/sharedsock/example/main.go index da62b276e..4fa1766b6 100644 --- a/sharedsock/example/main.go +++ b/sharedsock/example/main.go @@ -14,8 +14,8 @@ import ( func main() { port := 51820 - rawSock, err := sharedsock.Listen(port, sharedsock.NewIncomingSTUNFilter(), iface.DefaultMTU) - if err != nil { + rawSock, err := sharedsock.Listen(port, sharedsock.NewIncomingSTUNFilter(), iface.DefaultMTU) //nolint:staticcheck + if err != nil { //nolint:staticcheck // always errors on non-Linux builds panic(err) } diff --git a/util/file.go b/util/file.go index 73ad05b18..926904f9f 100644 --- a/util/file.go +++ b/util/file.go @@ -26,7 +26,7 @@ func WriteBytesWithRestrictedPermission(ctx context.Context, file string, bs []b return fmt.Errorf("enforce permission: %w", err) } - return writeBytes(ctx, file, err, configDir, configFileName, bs) + return writeBytes(ctx, file, configDir, configFileName, bs) } // WriteJsonWithRestrictedPermission writes JSON config object to a file. Enforces permission on the parent directory @@ -106,10 +106,10 @@ func writeJson(ctx context.Context, file string, obj interface{}, configDir stri return fmt.Errorf("marshal: %w", err) } - return writeBytes(ctx, file, err, configDir, configFileName, bs) + return writeBytes(ctx, file, configDir, configFileName, bs) } -func writeBytes(ctx context.Context, file string, err error, configDir string, configFileName string, bs []byte) error { +func writeBytes(ctx context.Context, file string, configDir string, configFileName string, bs []byte) error { if ctx.Err() != nil { return fmt.Errorf("write bytes start: %w", ctx.Err()) }