Skip to content

[client] Fix profile regressions in up --profile and status - #6479

Merged
riccardomanfrin merged 8 commits into
mainfrom
up_with_profile_notworking
Jun 19, 2026
Merged

riccardomanfrin merged 8 commits into
mainfrom
up_with_profile_notworking

Conversation

@riccardomanfrin

@riccardomanfrin riccardomanfrin commented Jun 19, 2026 •

Copy link
Copy Markdown
Contributor

Describe your changes

PR #6367 (migrating profile identity from display name to ID) introduced two regressions in the CLI profile flow. This PR fixes both.

  1. netbird up --profile <name> no longer auto-created a missing profile. Before [client] Migrate profile identity from display name to ID and allow renaming of profiles #6367, switching to a non-existent profile name simply recorded it as the active profile and the config file was created lazily. After the migration, the daemon resolves the supplied handle against existing profiles and returns codes.NotFound, which broke ephemeral CI workflows relying on netbird up --profile <name> --setup-key <key> to provision the profile on the fly (reported in discussion netbird up with profile and setup key fails on v0.73.0 #6476).

    upFunc in client/cmd/up.go now detects codes.NotFound from switchProfile, creates the profile via the daemon's existing AddProfile RPC (createProfile helper in client/cmd/login.go), and retries the switch. To make the status code inspectable by the caller, switchProfile now wraps the daemon error with %w instead of %v.

  2. netbird status showed an empty Profile: field for both the default and custom profiles. After [client] Migrate profile identity from display name to ID and allow renaming of profiles #6367, ProfileManager.GetActiveProfile returns only the profile ID (not the display name), and the CLI — running as the invoking user — cannot read the per-user profile files under /var/lib/netbird/<user>/ (root-owned, 0700) to resolve the name itself.

    client/cmd/status.go now resolves the display name through the daemon's existing GetActiveProfile RPC, which runs as root and maps the active ID to its display name. Added the getActiveProfileName helper and removed the now-unused profilemanager import.

Manually verified: netbird up --profile <new-name> --setup-key <key> provisions and connects, and netbird status shows the correct profile name for both default and custom profiles.

Issue ticket number and link

#6476 — regression report for netbird up --profile + setup-key failing on v0.73.0 after #6367.

Stack

Checklist

  • Is it a bug fix
  • Is a typo/documentation fix
  • Is a feature enhancement
  • It is a refactor
  • Created tests that fail without the change (if possible)

By submitting this pull request, you confirm that you have read and agree to the terms of the Contributor License Agreement.

Documentation

Select exactly one:

  • I added/updated documentation for this change
  • Documentation is not needed for this change (explain why)

These are regression fixes that restore the documented pre-0.73.0 behavior of netbird up --profile and the netbird status output. No public CLI flags, API surface, or configuration options change, so existing documentation remains accurate.

Docs PR URL (required if "docs added" is checked)

Paste the PR link from https://github.com/netbirdio/docs here:

N/A

Summary by CodeRabbit

  • Bug Fixes
    • Improved RPC error handling for profile-related operations, including clearer error wrapping during daemon failures.
  • New Features
    • The up command now switches to a specified profile, and if it doesn’t exist, creates it and then retries activation.
  • Improvements
    • The status command now pulls the active profile’s display name from the running daemon.
    • Profile creation output is more reliable by using the daemon-returned profile ID after creation.

@coderabbitai

coderabbitai Bot commented Jun 19, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

addProfileFunc in profile.go extracts a new addProfileOnDaemon helper that encapsulates daemon profile creation. switchProfile error wrapping is changed from %v to %w. A new createProfile gRPC helper invokes daemon AddProfile via the extracted helper. A new switchOrCreateProfile orchestration helper attempts profile switching, and on NotFound it auto-creates the profile and retries. upFunc now delegates profile resolution to this helper. statusFunc resolves the active profile name via a new getActiveProfileName daemon RPC helper, removing the local profilemanager dependency.

Changes

Profile management via daemon RPC

Layer / File(s) Summary
Extract addProfileOnDaemon helper in profile.go
client/cmd/profile.go
addProfileFunc refactored to fetch current user earlier, dial and construct daemon client, delegate profile creation to new addProfileOnDaemon helper that returns the created profile ID, and use the returned ID in success output.
createProfile helper and switchProfile error wrapping
client/cmd/login.go, client/cmd/up.go
switchProfile now uses %w for error wrapping. New createProfile function dials the daemon gRPC service and invokes AddProfile via addProfileOnDaemon, returning wrapped errors on connection or RPC failure.
switchOrCreateProfile auto-create and retry orchestration
client/cmd/up.go
New helper attempts switchProfile; on codes.NotFound it calls createProfile and retries switchProfile, then persists the resolved profile via pm.SwitchProfile.
upFunc integration of switchOrCreateProfile
client/cmd/up.go
upFunc replaces the previous switchProfile + pm.SwitchProfile two-step with a single switchOrCreateProfile call that handles both resolution and auto-creation.
Active profile name resolved via daemon RPC in statusFunc
client/cmd/status.go
profilemanager import removed. statusFunc calls new getActiveProfileName(ctx) helper, which dials the daemon, invokes GetActiveProfile, and returns "" on any connection or RPC error.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • netbirdio/netbird#6367: Directly related daemon/API-driven profile identity migration that introduces the profile add/switch behavior in client/cmd/login.go and client/cmd/up.go that this PR builds upon.

Poem

🐇 When profiles go missing, no need to despair!
We try, we create, we retry with care.
switchOrCreateProfile dances with grace,
Auto-creating each lost profile case.
Status now asks the daemon, not the stack,
gRPC calls bring the profile name back. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically identifies the two main regressions being fixed: profile auto-creation in up --profile and the empty profile display in status.
Description check ✅ Passed The description comprehensively addresses all template requirements: changes are well-documented, issue reference is provided, the bug fix checkbox is marked, and documentation status is properly justified.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch up_with_profile_notworking

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Extract the profile switch/auto-create logic from upFunc into a dedicated
switchOrCreateProfile helper. The inlined NotFound-retry branch pushed
upFunc over SonarCloud's cognitive complexity threshold (S3776).
No behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 19, 2026 •

Copy link
Copy Markdown

Release artifacts

Built for PR head d3e7c0b in workflow run #16048.

Artifact Link
All release artifacts Download
Linux packages Download
Windows packages Download
macOS packages Download
UI artifacts Download
UI macOS artifacts Download

GHCR images (amd64)

No GHCR images were pushed.

This comment is updated by the Release workflow. Artifact links expire according to the workflow retention policy.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@client/cmd/login.go`:
- Around line 247-252: The code has a race condition where another concurrent
process can create the profile between the check and the createProfile call on
line 247. If createProfile fails because the profile already exists, the code
should still attempt switchProfile since it would succeed, rather than returning
the error immediately. Modify the error handling after the createProfile call to
check if the error indicates the profile already exists (e.g., a NotFound or
AlreadyExists error), and if so, proceed to call switchProfile anyway. Only
return the error if switchProfile also fails or if createProfile failed for a
different reason that should be fatal.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 937d7b9a-e868-4d89-94b7-a2cd0730d828

📥 Commits

Reviewing files that changed from the base of the PR and between 5830c9e and a986551.

📒 Files selected for processing (2)
  • client/cmd/login.go
  • client/cmd/up.go

Comment thread client/cmd/login.go Resolved
Don't fail switchOrCreateProfile on a createProfile error: a concurrent
run may create the profile between the NotFound check and our create
call. Retry the switch regardless and only surface the create error if
the switch also fails. Addresses CodeRabbit race-condition feedback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@riccardomanfrin
riccardomanfrin requested a review from lixmal June 19, 2026 11:16
pappz
pappz previously approved these changes Jun 19, 2026
theodorsm
theodorsm previously approved these changes Jun 19, 2026
@riccardomanfrin
riccardomanfrin dismissed stale reviews from theodorsm and pappz via 88c2d16 June 19, 2026 13:26
Comment thread client/cmd/login.go Outdated
Comment on lines +266 to +292
// createProfile dials the daemon and creates a new profile with the given
// display name, returning its generated ID. Use addProfileOnDaemon directly
// when a daemon client is already available to reuse the connection.
func createProfile(ctx context.Context, profileName, username string) (profilemanager.ID, error) {
conn, err := DialClientGRPCServer(ctx, daemonAddr)
if err != nil {
//nolint
return "", fmt.Errorf("failed to connect to daemon error: %v\n"+
"If the daemon is not running please run: "+
"\nnetbird service install \nnetbird service start\n", err)
}
defer conn.Close()

return addProfileOnDaemon(ctx, proto.NewDaemonServiceClient(conn), profileName, username)
}

// addProfileOnDaemon issues the AddProfile RPC on an existing daemon client
// and returns the new profile's ID. It is the single entry point for profile
// creation, shared by `netbird profile add` and the `netbird up --profile
// <name>` auto-create path.
func addProfileOnDaemon(ctx context.Context, client proto.DaemonServiceClient, profileName, username string) (profilemanager.ID, error) {
resp, err := client.AddProfile(ctx, &proto.AddProfileRequest{
ProfileName: profileName,
Username: username,
})
if err != nil {
return "", fmt.Errorf("add profile failed: %w", err)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move these helper functions to client/cmd/profile.go.

Comment thread client/cmd/login.go Outdated
Comment on lines +236 to +264
// switchOrCreateProfile switches the active profile to the one identified by
// handle, creating it first when it does not exist yet. This restores the
// pre-0.73 behaviour where `netbird up --profile <name>` auto-creates a
// missing profile instead of failing.
func switchOrCreateProfile(ctx context.Context, pm *profilemanager.ProfileManager, handle, username string) error {
resolvedID, err := switchProfile(ctx, handle, username)
if err != nil {
st, ok := gstatus.FromError(err)
if !ok || st.Code() != codes.NotFound {
return err
}
// Don't fail immediately on a create error: a concurrent run may
// have created the profile between the NotFound above and this
// call, in which case the retried switch still succeeds. Only
// surface the create error if the switch also fails.
_, createErr := createProfile(ctx, handle, username)
if resolvedID, err = switchProfile(ctx, handle, username); err != nil {
if createErr != nil {
return fmt.Errorf("create profile: %w", createErr)
}
return err
}
}

if err := pm.SwitchProfile(resolvedID); err != nil {
return err
}
return nil
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move this helper function to client/cmd/up.go.

theodorsm
theodorsm previously approved these changes Jun 19, 2026

@theodorsm theodorsm left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested and works :shipit:

@riccardomanfrin
riccardomanfrin merged commit 883a1a8 into main Jun 19, 2026
43 checks passed
@riccardomanfrin
riccardomanfrin deleted the up_with_profile_notworking branch June 19, 2026 14:23
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants