@@ -48,6 +48,7 @@ type deregisterDeps struct {
4848 platform externalnode.PlatformChecker
4949 prompter terminal.Selector
5050 confirmer terminal.Confirmer
51+ gater sudo.Gater
5152 netbird register.NetBirdManager
5253 nodeClients externalnode.NodeClientFactory
5354 registrationStore register.RegistrationStore
@@ -59,6 +60,7 @@ func defaultDeregisterDeps() deregisterDeps {
5960 platform : register.LinuxPlatform {},
6061 prompter : register.TerminalPrompter {},
6162 confirmer : register.TerminalPrompter {},
63+ gater : sudo .Default ,
6264 netbird : register.Netbird {},
6365 nodeClients : register.DefaultNodeClientFactory {},
6466 registrationStore : register .NewFileRegistrationStore (),
@@ -76,6 +78,8 @@ the Brev tunnel (network agent).`
7678)
7779
7880func NewCmdDeregister (t * terminal.Terminal , store DeregisterStore ) * cobra.Command {
81+ var approveFlag bool
82+
7983 cmd := & cobra.Command {
8084 Annotations : map [string ]string {"configuration" : "" },
8185 Use : "deregister" ,
@@ -84,65 +88,110 @@ func NewCmdDeregister(t *terminal.Terminal, store DeregisterStore) *cobra.Comman
8488 Long : deregisterLong ,
8589 Example : deregisterExample ,
8690 RunE : func (cmd * cobra.Command , args []string ) error {
87- return runDeregister (cmd .Context (), t , store , defaultDeregisterDeps ())
91+ return runDeregister (cmd .Context (), t , store , defaultDeregisterDeps (), approveFlag )
8892 },
8993 }
9094
95+ cmd .Flags ().BoolVar (& approveFlag , "approve" , false , "skip confirmation prompt (assume yes)" )
96+
9197 return cmd
9298}
9399
94- func runDeregister (ctx context.Context , t * terminal.Terminal , s DeregisterStore , deps deregisterDeps ) error { //nolint:funlen // deregistration flow
100+ func runDeregister (ctx context.Context , t * terminal.Terminal , s DeregisterStore , deps deregisterDeps , skipConfirm bool ) error { //nolint:funlen,gocyclo // deregistration flow
95101 if ! deps .platform .IsCompatible () {
96102 return fmt .Errorf ("brev deregister is only supported on Linux" )
97103 }
98104
99- if err := sudo . Gate (t , deps .confirmer , "Device deregistration" ); err != nil {
105+ if err := deps . gater . Gate (t , deps .confirmer , "Device deregistration" , skipConfirm ); err != nil {
100106 return fmt .Errorf ("sudo issue: %w" , err )
101107 }
102108
103109 reg , err := deps .registrationStore .Load ()
104110 if err != nil {
111+ return err //nolint:wrapcheck // do not present stack trace for this error
112+ }
113+
114+ // Only prompt for login when there is a device to deregister.
115+ if _ , err := s .GetCurrentUser (); err != nil {
105116 return breverrors .WrapAndTrace (err )
106117 }
107118
119+ orgName := reg .OrgName
120+ if orgName == "" {
121+ orgName = "(unknown)"
122+ }
123+ osUser , _ := user .Current ()
124+ linuxUser := "(unknown)"
125+ if osUser != nil {
126+ linuxUser = osUser .Username
127+ }
128+
108129 t .Vprint ("" )
109- t .Vprint (t .Green ("Deregistering device" ))
130+ t .Vprint (t .White ("══════════════════════════════════════════════════" ))
131+ t .Vprint (t .White (" Deregistering your device from Brev" ))
132+ t .Vprint (t .White ("══════════════════════════════════════════════════" ))
133+ t .Vprint ("" )
134+ if ! skipConfirm {
135+ t .Vprint (t .Green (" Please confirm before continuing:" ))
136+ t .Vprint ("" )
137+ }
138+ t .Vprintf (" %s %s\n " , t .Green (fmt .Sprintf ("%-14s" , "Device:" )), t .BoldBlue (reg .DisplayName + " (" + reg .ExternalNodeID + ")" ))
139+ t .Vprintf (" %s %s\n " , t .Green (fmt .Sprintf ("%-14s" , "Organization:" )), t .BoldBlue (orgName + " (" + reg .OrgID + ")" ))
140+ t .Vprintf (" %s %s\n " , t .Green (fmt .Sprintf ("%-14s" , "Linux user:" )), t .BoldBlue (linuxUser ))
110141 t .Vprint ("" )
111- t .Vprintf (" Node ID: %s\n " , reg .ExternalNodeID )
112- t .Vprintf (" Name: %s\n " , reg .DisplayName )
142+ t .Vprint (t .Yellow (" This will:" ))
143+ t .Vprint (" 1. Remove this node from Brev" )
144+ t .Vprint (" 2. Remove Brev SSH keys from this machine (if any)" )
145+ t .Vprint (" 3. Uninstall the Brev tunnel" )
146+ t .Vprint (" 4. Delete local registration data" )
113147 t .Vprint ("" )
114148
115- confirm := deps .prompter .Select (
116- "Proceed with deregistration?" ,
117- []string {"Yes, proceed" , "No, cancel" },
118- )
119- if confirm != "Yes, proceed" {
120- t .Vprint ("Deregistration canceled." )
121- return nil
149+ if ! skipConfirm {
150+ confirm := deps .prompter .Select (
151+ "Proceed with deregistration?" ,
152+ []string {"Yes, proceed" , "No, cancel" },
153+ )
154+ if confirm != "Yes, proceed" {
155+ t .Vprint ("Deregistration canceled." )
156+ return nil
157+ }
122158 }
123159
124- t .Vprint ("" )
125- t .Vprint (t .Yellow ("Removing node from Brev..." ))
160+ const clearLine = "\033 [2K\n "
161+
162+ t .Vprint (t .Yellow ("[Step 1/4] Removing node from Brev..." ))
163+ sp := t .NewSpinner ()
164+ sp .Suffix = " Deregistering device..."
165+ sp .Start ()
126166 client := deps .nodeClients .NewNodeClient (s , config .GlobalConfig .GetBrevPublicAPIURL ())
127- if _ , err : = client .RemoveNode (ctx , connect .NewRequest (& nodev1.RemoveNodeRequest {
167+ _ , err = client .RemoveNode (ctx , connect .NewRequest (& nodev1.RemoveNodeRequest {
128168 ExternalNodeId : reg .ExternalNodeID ,
129- })); err != nil {
169+ }))
170+ sp .FinalMSG = clearLine
171+ sp .Stop ()
172+ if err != nil {
130173 return fmt .Errorf ("failed to deregister node: %w" , err )
131174 }
132- t .Vprint ( t . Green ( " Node removed from Brev." ))
175+ t .Vprintf ( "%s Node removed from Brev.\n " , t . Green ( " ✓ " ))
133176 t .Vprint ("" )
134177
135- // Remove Brev SSH keys from authorized_keys.
136- osUser , err := user .Current ()
137- if err != nil {
138- t .Vprintf (" Warning: could not determine current user for SSH key cleanup: %v\n " , err )
178+ t .Vprint (t .Yellow ("[Step 2/4] Removing Brev SSH keys..." ))
179+ sp = t .NewSpinner ()
180+ sp .Suffix = " Deregistering device..."
181+ sp .Start ()
182+ if osUser == nil {
183+ sp .FinalMSG = clearLine
184+ sp .Stop ()
185+ t .Vprintf (" %s\n " , t .Yellow ("Skipped: could not determine current user" ))
139186 } else {
140187 removed , kerr := deps .sshKeys .RemoveBrevKeys (osUser )
188+ sp .FinalMSG = clearLine
189+ sp .Stop ()
141190 switch {
142191 case kerr != nil :
143- t .Vprintf (" Warning: failed to remove Brev SSH keys: %v\n " , kerr )
192+ t .Vprintf (" %s \n " , t . Yellow ( fmt . Sprintf ( " Warning: failed to remove Brev SSH keys: %v" , kerr )) )
144193 case len (removed ) > 0 :
145- t .Vprint ( t . Green ( " Brev SSH keys removed from authorized_keys:" ))
194+ t .Vprintf ( "%s Brev SSH keys removed from authorized_keys:\n " , t . Green ( " ✓ " ))
146195 for _ , key := range removed {
147196 t .Vprintf (" - %s\n " , key )
148197 }
@@ -152,21 +201,34 @@ func runDeregister(ctx context.Context, t *terminal.Terminal, s DeregisterStore,
152201 }
153202 t .Vprint ("" )
154203
155- t .Vprint ("Removing Brev tunnel..." )
156- if err := deps .netbird .Uninstall (); err != nil {
157- t .Vprintf (" Warning: failed to remove Brev tunnel: %v\n " , err )
204+ t .Vprint (t .Yellow ("[Step 3/4] Removing Brev tunnel..." ))
205+ sp = t .NewSpinner ()
206+ sp .Suffix = " Deregistering device..."
207+ sp .Start ()
208+ err = deps .netbird .Uninstall ()
209+ sp .FinalMSG = clearLine
210+ sp .Stop ()
211+ if err != nil {
212+ t .Vprintf (" %s\n " , t .Yellow (fmt .Sprintf ("Warning: failed to remove Brev tunnel: %v" , err )))
158213 } else {
159- t .Vprint ( t . Green ( " Brev tunnel removed." ))
214+ t .Vprintf ( "%s Brev tunnel removed.\n " , t . Green ( " ✓ " ))
160215 }
161216 t .Vprint ("" )
162217
163- t .Vprint ("Removing registration data..." )
164- if err := deps .registrationStore .Delete (); err != nil {
165- t .Vprintf (" Warning: failed to remove local registration file: %v\n " , err )
218+ t .Vprint (t .Yellow ("[Step 4/4] Removing registration data..." ))
219+ sp = t .NewSpinner ()
220+ sp .Suffix = " Deregistering device..."
221+ sp .Start ()
222+ err = deps .registrationStore .Delete ()
223+ sp .FinalMSG = clearLine
224+ sp .Stop ()
225+ if err != nil {
226+ t .Vprintf (" %s\n " , t .Yellow (fmt .Sprintf ("Warning: failed to remove local registration file: %v" , err )))
166227 t .Vprint (" You can manually remove it with: rm /etc/brev/device_registration.json" )
228+ } else {
229+ t .Vprintf ("%s Registration data removed.\n " , t .Green (" ✓" ))
167230 }
168-
169- t .Vprint (t .Green ("Deregistration complete." ))
231+ t .Vprintf ("%s Deregistration complete.\n " , t .Green (" ✓" ))
170232 t .Vprint ("" )
171233
172234 return nil
0 commit comments