Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
cc595da
[management/client] create job channel between management and client…
aliamerj Aug 28, 2025
17a2af9
implement remote debug api (#4418)
aliamerj Aug 29, 2025
d01c3d5
[management/client] Integrate Job API with Job Stream and Client Engi…
aliamerj Sep 23, 2025
bdae55a
update yml for job (#4532)
aliamerj Sep 23, 2025
5a12c5d
fix everything
aliamerj Oct 6, 2025
60c5782
fix other lint issue
aliamerj Oct 6, 2025
7a95bf5
fix bug with missing logs file
aliamerj Oct 10, 2025
23f9dd0
clean up
aliamerj Oct 10, 2025
e570570
add some info level log
aliamerj Oct 10, 2025
0e9438d
fix lint
aliamerj Oct 10, 2025
536b000
handle unimplemented Job stream
aliamerj Oct 18, 2025
976787d
Merge remote-tracking branch 'upstream/main' into feature/remote-debu…
aliamerj Oct 30, 2025
49d36b7
rename logFile to logPath
aliamerj Nov 4, 2025
13febbb
update event metadata for creating new job
aliamerj Nov 4, 2025
6d6f090
Merge branch 'main' into feature/remote-debug-clean
pappz Nov 12, 2025
df14f13
Fix MockAccountManager function calls checks
pappz Nov 12, 2025
8fc4fed
Fix SQL query syntax
pappz Nov 12, 2025
06bb865
Fix validation
pappz Nov 12, 2025
c8bc865
Fix error message
pappz Nov 12, 2025
6898e57
Fix nil pointer check
pappz Nov 12, 2025
1d2a537
remove EOF skipping
aliamerj Nov 12, 2025
aa39a5d
Add logs
pappz Nov 14, 2025
22d7960
Add profile name and events for status
pappz Nov 17, 2025
9cdfb0d
Handle unimplemented job type
pappz Nov 17, 2025
a1457f5
Handle job error responses on mgm side
pappz Nov 17, 2025
f363967
Fix string conversation
pappz Nov 17, 2025
554c9bc
Do not expect last sync for debug bundle
pappz Nov 17, 2025
39bec2d
Truncate too long error response
pappz Nov 17, 2025
938554f
Implement time for parameter usage
pappz Nov 17, 2025
a939c17
Fix error message
pappz Nov 17, 2025
fa6151b
Fix error message
pappz Nov 17, 2025
2765bcf
Restore in case of error
pappz Nov 17, 2025
c573645
Fix log message
pappz Nov 18, 2025
b03154d
Use dedicated ctx in stream
pappz Nov 18, 2025
f9f6409
Remove context log from grpc client
pappz Nov 18, 2025
fe88a56
Fix log message
pappz Nov 18, 2025
224bd8f
Merge branch 'main' into feature/remote-debug-clean
pappz Nov 18, 2025
2dec76f
Fix test after merge conflict
pappz Nov 18, 2025
9a80824
Fix lint issue
pappz Nov 18, 2025
f36e206
Fix account moc
pappz Nov 18, 2025
3d3b05c
Disable lint for deprecated line
pappz Nov 18, 2025
14b5637
Fix error message
pappz Nov 18, 2025
d63f2e5
Typo fix
pappz Nov 18, 2025
194951c
Typo fix
pappz Nov 19, 2025
ff95857
Fix permission checks
pappz Nov 19, 2025
60d2a2c
Fix error handling
pappz Nov 19, 2025
0190cca
fix use old oapi-codegen version
pascal-fischer Nov 19, 2025
7530712
Revert "fix use old oapi-codegen version"
pascal-fischer Nov 19, 2025
9f9a295
Merge branch 'main' into feature/remote-debug-clean
pappz Nov 19, 2025
3a91e16
Extend the wasm artifact size limit
pappz Nov 19, 2025
a646d54
Refactor job channel management to prevent panics (#4824)
pappz Nov 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/wasm-build-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ jobs:

echo "Size: ${SIZE} bytes (${SIZE_MB} MB)"

if [ ${SIZE} -gt 52428800 ]; then
echo "Wasm binary size (${SIZE_MB}MB) exceeds 50MB limit!"
if [ ${SIZE} -gt 57671680 ]; then
echo "Wasm binary size (${SIZE_MB}MB) exceeds 55MB limit!"
exit 1
fi

29 changes: 1 addition & 28 deletions client/cmd/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
"github.com/netbirdio/netbird/client/internal/profilemanager"
"github.com/netbirdio/netbird/client/proto"
"github.com/netbirdio/netbird/client/server"
nbstatus "github.com/netbirdio/netbird/client/status"
mgmProto "github.com/netbirdio/netbird/shared/management/proto"
"github.com/netbirdio/netbird/upload-server/types"
)
Expand Down Expand Up @@ -98,7 +97,6 @@ func debugBundle(cmd *cobra.Command, _ []string) error {
client := proto.NewDaemonServiceClient(conn)
request := &proto.DebugBundleRequest{
Anonymize: anonymizeFlag,
Status: getStatusOutput(cmd, anonymizeFlag),
SystemInfo: systemInfoFlag,
LogFileCount: logFileCount,
}
Expand Down Expand Up @@ -220,21 +218,15 @@ func runForDuration(cmd *cobra.Command, args []string) error {

time.Sleep(3 * time.Second)

headerPostUp := fmt.Sprintf("----- NetBird post-up - Timestamp: %s", time.Now().Format(time.RFC3339))
statusOutput := fmt.Sprintf("%s\n%s", headerPostUp, getStatusOutput(cmd, anonymizeFlag))

if waitErr := waitForDurationOrCancel(cmd.Context(), duration, cmd); waitErr != nil {
return waitErr
}
cmd.Println("\nDuration completed")

cmd.Println("Creating debug bundle...")

headerPreDown := fmt.Sprintf("----- NetBird pre-down - Timestamp: %s - Duration: %s", time.Now().Format(time.RFC3339), duration)
statusOutput = fmt.Sprintf("%s\n%s\n%s", statusOutput, headerPreDown, getStatusOutput(cmd, anonymizeFlag))
request := &proto.DebugBundleRequest{
Anonymize: anonymizeFlag,
Status: statusOutput,
SystemInfo: systemInfoFlag,
LogFileCount: logFileCount,
}
Expand Down Expand Up @@ -301,25 +293,6 @@ func setSyncResponsePersistence(cmd *cobra.Command, args []string) error {
return nil
}

func getStatusOutput(cmd *cobra.Command, anon bool) string {
var statusOutputString string
statusResp, err := getStatus(cmd.Context(), true)
if err != nil {
cmd.PrintErrf("Failed to get status: %v\n", err)
} else {
pm := profilemanager.NewProfileManager()
var profName string
if activeProf, err := pm.GetActiveProfile(); err == nil {
profName = activeProf.Name
}

statusOutputString = nbstatus.ParseToFullDetailSummary(
nbstatus.ConvertToStatusOutputOverview(statusResp, anon, "", nil, nil, nil, "", profName),
)
}
return statusOutputString
}

func waitForDurationOrCancel(ctx context.Context, duration time.Duration, cmd *cobra.Command) error {
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
Expand Down Expand Up @@ -378,7 +351,7 @@ func generateDebugBundle(config *profilemanager.Config, recorder *peer.Status, c
InternalConfig: config,
StatusRecorder: recorder,
SyncResponse: syncResponse,
LogFile: logFilePath,
LogPath: logFilePath,
},
debug.BundleConfig{
IncludeSystemInfo: true,
Expand Down
2 changes: 1 addition & 1 deletion client/cmd/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func statusFunc(cmd *cobra.Command, args []string) error {
profName = activeProf.Name
}

var outputInformationHolder = nbstatus.ConvertToStatusOutputOverview(resp, anonymizeFlag, statusFilter, prefixNamesFilter, prefixNamesFilterMap, ipsFilterMap, connectionTypeFilter, profName)
var outputInformationHolder = nbstatus.ConvertToStatusOutputOverview(resp.GetFullStatus(), anonymizeFlag, resp.GetDaemonVersion(), statusFilter, prefixNamesFilter, prefixNamesFilterMap, ipsFilterMap, connectionTypeFilter, profName)
var statusOutputString string
switch {
case detailFlag:
Expand Down
6 changes: 4 additions & 2 deletions client/cmd/testutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/netbirdio/netbird/management/internals/controllers/network_map/controller"
"github.com/netbirdio/netbird/management/internals/controllers/network_map/update_channel"
nbgrpc "github.com/netbirdio/netbird/management/internals/shared/grpc"
"github.com/netbirdio/netbird/management/server/job"

clientProto "github.com/netbirdio/netbird/client/proto"
client "github.com/netbirdio/netbird/client/server"
Expand Down Expand Up @@ -88,6 +89,7 @@ func startManagement(t *testing.T, config *config.Config, testFile string) (*grp
}
t.Cleanup(cleanUp)

jobManager := job.NewJobManager(nil, store)
eventStore := &activity.InMemoryEventStore{}
if err != nil {
return nil, nil
Expand Down Expand Up @@ -118,13 +120,13 @@ func startManagement(t *testing.T, config *config.Config, testFile string) (*grp
requestBuffer := mgmt.NewAccountRequestBuffer(ctx, store)
networkMapController := controller.NewController(ctx, store, metrics, updateManager, requestBuffer, mgmt.MockIntegratedValidator{}, settingsMockManager, "netbird.cloud", port_forwarding.NewControllerMock(), config)

accountManager, err := mgmt.BuildManager(context.Background(), config, store, networkMapController, nil, "", eventStore, nil, false, iv, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock, false)
accountManager, err := mgmt.BuildManager(context.Background(), config, store, networkMapController, jobManager, nil, "", eventStore, nil, false, iv, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock, false)
if err != nil {
t.Fatal(err)
}

secretsManager := nbgrpc.NewTimeBasedAuthSecretsManager(updateManager, config.TURNConfig, config.Relay, settingsMockManager, groupsManager)
mgmtServer, err := nbgrpc.NewServer(config, accountManager, settingsMockManager, updateManager, secretsManager, nil, &manager.EphemeralManager{}, nil, &mgmt.MockIntegratedValidator{}, networkMapController)
mgmtServer, err := nbgrpc.NewServer(config, accountManager, settingsMockManager, updateManager, jobManager, secretsManager, nil, &manager.EphemeralManager{}, nil, &mgmt.MockIntegratedValidator{}, networkMapController)
if err != nil {
t.Fatal(err)
}
Expand Down
2 changes: 1 addition & 1 deletion client/cmd/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ func runInForegroundMode(ctx context.Context, cmd *cobra.Command, activeProf *pr
connectClient := internal.NewConnectClient(ctx, config, r)
SetupDebugHandler(ctx, config, r, connectClient, "")

return connectClient.Run(nil)
return connectClient.Run(nil, util.FindFirstLogPath(logFiles))
}

func runInDaemonMode(ctx context.Context, cmd *cobra.Command, pm *profilemanager.ProfileManager, activeProf *profilemanager.Profile, profileSwitched bool) error {
Expand Down
3 changes: 2 additions & 1 deletion client/embed/embed.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,14 +173,15 @@ func (c *Client) Start(startCtx context.Context) error {
}

recorder := peer.NewRecorder(c.config.ManagementURL.String())

client := internal.NewConnectClient(ctx, c.config, recorder)

// either startup error (permanent backoff err) or nil err (successful engine up)
// TODO: make after-startup backoff err available
run := make(chan struct{})
clientErr := make(chan error, 1)
go func() {
if err := client.Run(run); err != nil {
if err := client.Run(run, ""); err != nil {
Comment thread
pappz marked this conversation as resolved.
clientErr <- err
}
}()
Expand Down
22 changes: 12 additions & 10 deletions client/internal/connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ func NewConnectClient(
ctx context.Context,
config *profilemanager.Config,
statusRecorder *peer.Status,

) *ConnectClient {
return &ConnectClient{
ctx: ctx,
Expand All @@ -63,8 +62,8 @@ func NewConnectClient(
}

// Run with main logic.
func (c *ConnectClient) Run(runningChan chan struct{}) error {
return c.run(MobileDependency{}, runningChan)
func (c *ConnectClient) Run(runningChan chan struct{}, logPath string) error {
return c.run(MobileDependency{}, runningChan, logPath)
}

// RunOnAndroid with main logic on mobile system
Expand All @@ -83,7 +82,7 @@ func (c *ConnectClient) RunOnAndroid(
HostDNSAddresses: dnsAddresses,
DnsReadyListener: dnsReadyListener,
}
return c.run(mobileDependency, nil)
return c.run(mobileDependency, nil, "")
}

func (c *ConnectClient) RunOniOS(
Expand All @@ -101,10 +100,10 @@ func (c *ConnectClient) RunOniOS(
DnsManager: dnsManager,
StateFilePath: stateFilePath,
}
return c.run(mobileDependency, nil)
return c.run(mobileDependency, nil, "")
}

func (c *ConnectClient) run(mobileDependency MobileDependency, runningChan chan struct{}) error {
func (c *ConnectClient) run(mobileDependency MobileDependency, runningChan chan struct{}, logPath string) error {
defer func() {
if r := recover(); r != nil {
rec := c.statusRecorder
Expand Down Expand Up @@ -247,7 +246,7 @@ func (c *ConnectClient) run(mobileDependency MobileDependency, runningChan chan
relayURLs, token := parseRelayInfo(loginResp)
peerConfig := loginResp.GetPeerConfig()

engineConfig, err := createEngineConfig(myPrivateKey, c.config, peerConfig)
engineConfig, err := createEngineConfig(myPrivateKey, c.config, peerConfig, logPath)
if err != nil {
log.Error(err)
return wrapErr(err)
Expand All @@ -271,7 +270,7 @@ func (c *ConnectClient) run(mobileDependency MobileDependency, runningChan chan
checks := loginResp.GetChecks()

c.engineMutex.Lock()
c.engine = NewEngine(engineCtx, cancel, signalClient, mgmClient, relayManager, engineConfig, mobileDependency, c.statusRecorder, checks)
c.engine = NewEngine(engineCtx, cancel, signalClient, mgmClient, relayManager, engineConfig, mobileDependency, c.statusRecorder, checks, c.config)
c.engine.SetSyncResponsePersistence(c.persistSyncResponse)
c.engineMutex.Unlock()

Expand Down Expand Up @@ -410,7 +409,7 @@ func (c *ConnectClient) SetSyncResponsePersistence(enabled bool) {
}

// createEngineConfig converts configuration received from Management Service to EngineConfig
func createEngineConfig(key wgtypes.Key, config *profilemanager.Config, peerConfig *mgmProto.PeerConfig) (*EngineConfig, error) {
func createEngineConfig(key wgtypes.Key, config *profilemanager.Config, peerConfig *mgmProto.PeerConfig, logPath string) (*EngineConfig, error) {
nm := false
if config.NetworkMonitor != nil {
nm = *config.NetworkMonitor
Expand Down Expand Up @@ -445,7 +444,10 @@ func createEngineConfig(key wgtypes.Key, config *profilemanager.Config, peerConf

LazyConnectionEnabled: config.LazyConnectionEnabled,

MTU: selectMTU(config.MTU, peerConfig.Mtu),
MTU: selectMTU(config.MTU, peerConfig.Mtu),
LogPath: logPath,

ProfileConfig: config,
}

if config.PreSharedKey != "" {
Expand Down
45 changes: 26 additions & 19 deletions client/internal/debug/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ import (
"github.com/netbirdio/netbird/client/anonymize"
"github.com/netbirdio/netbird/client/internal/peer"
"github.com/netbirdio/netbird/client/internal/profilemanager"
nbstatus "github.com/netbirdio/netbird/client/status"
mgmProto "github.com/netbirdio/netbird/shared/management/proto"
"github.com/netbirdio/netbird/util"
"github.com/netbirdio/netbird/version"
)

const readmeContent = `Netbird debug bundle
Expand Down Expand Up @@ -218,10 +220,9 @@ type BundleGenerator struct {
internalConfig *profilemanager.Config
statusRecorder *peer.Status
syncResponse *mgmProto.SyncResponse
logFile string
logPath string

anonymize bool
clientStatus string
includeSystemInfo bool
logFileCount uint32

Expand All @@ -230,7 +231,6 @@ type BundleGenerator struct {

type BundleConfig struct {
Anonymize bool
ClientStatus string
IncludeSystemInfo bool
LogFileCount uint32
}
Expand All @@ -239,7 +239,7 @@ type GeneratorDependencies struct {
InternalConfig *profilemanager.Config
StatusRecorder *peer.Status
SyncResponse *mgmProto.SyncResponse
LogFile string
LogPath string
}

func NewBundleGenerator(deps GeneratorDependencies, cfg BundleConfig) *BundleGenerator {
Expand All @@ -255,10 +255,9 @@ func NewBundleGenerator(deps GeneratorDependencies, cfg BundleConfig) *BundleGen
internalConfig: deps.InternalConfig,
statusRecorder: deps.StatusRecorder,
syncResponse: deps.SyncResponse,
logFile: deps.LogFile,
logPath: deps.LogPath,

anonymize: cfg.Anonymize,
clientStatus: cfg.ClientStatus,
includeSystemInfo: cfg.IncludeSystemInfo,
logFileCount: logFileCount,
}
Expand Down Expand Up @@ -304,13 +303,6 @@ func (g *BundleGenerator) createArchive() error {
return fmt.Errorf("add status: %w", err)
}

if g.statusRecorder != nil {
status := g.statusRecorder.GetFullStatus()
seedFromStatus(g.anonymizer, &status)
} else {
log.Debugf("no status recorder available for seeding")
}

if err := g.addConfig(); err != nil {
log.Errorf("failed to add config to debug bundle: %v", err)
}
Expand Down Expand Up @@ -343,7 +335,7 @@ func (g *BundleGenerator) createArchive() error {
log.Errorf("failed to add wg show output: %v", err)
}

if g.logFile != "" && !slices.Contains(util.SpecialLogs, g.logFile) {
if g.logPath != "" && !slices.Contains(util.SpecialLogs, g.logPath) {
if err := g.addLogfile(); err != nil {
log.Errorf("failed to add log file to debug bundle: %v", err)
if err := g.trySystemdLogFallback(); err != nil {
Expand Down Expand Up @@ -388,11 +380,26 @@ func (g *BundleGenerator) addReadme() error {
}

func (g *BundleGenerator) addStatus() error {
if status := g.clientStatus; status != "" {
statusReader := strings.NewReader(status)
if g.statusRecorder != nil {
pm := profilemanager.NewProfileManager()
var profName string
if activeProf, err := pm.GetActiveProfile(); err == nil {
profName = activeProf.Name
}

fullStatus := g.statusRecorder.GetFullStatus()
protoFullStatus := nbstatus.ToProtoFullStatus(fullStatus)
protoFullStatus.Events = g.statusRecorder.GetEventHistory()
overview := nbstatus.ConvertToStatusOutputOverview(protoFullStatus, g.anonymize, version.NetbirdVersion(), "", nil, nil, nil, "", profName)
statusOutput := nbstatus.ParseToFullDetailSummary(overview)

statusReader := strings.NewReader(statusOutput)
if err := g.addFileToZip(statusReader, "status.txt"); err != nil {
return fmt.Errorf("add status file to zip: %w", err)
}
seedFromStatus(g.anonymizer, &fullStatus)
} else {
log.Debugf("no status recorder available for seeding")
}
return nil
}
Expand Down Expand Up @@ -662,14 +669,14 @@ func (g *BundleGenerator) addCorruptedStateFiles() error {
}

func (g *BundleGenerator) addLogfile() error {
if g.logFile == "" {
if g.logPath == "" {
log.Debugf("skipping empty log file in debug bundle")
return nil
}

logDir := filepath.Dir(g.logFile)
logDir := filepath.Dir(g.logPath)

if err := g.addSingleLogfile(g.logFile, clientLogFile); err != nil {
if err := g.addSingleLogfile(g.logPath, clientLogFile); err != nil {
return fmt.Errorf("add client log file to zip: %w", err)
}

Expand Down
Loading
Loading