Skip to content

Commit f9b73f0

Browse files
committed
feat: interactive unregister confirmation before re-registration
When `ggo agent register` is run on an already-registered machine: - Interactive mode: shows current agent ID and prompts to confirm unregister before proceeding with the new registration - --force flag: auto-unregisters without prompting (for scripts) - JSON mode: returns ErrAlreadyRegistered (requires --force) - Stale config (agent deleted from server): auto-clears and proceeds Install scripts now pass --force so re-installation works seamlessly. Made-with: Cursor
1 parent 743646b commit f9b73f0

3 files changed

Lines changed: 40 additions & 28 deletions

File tree

cmd/ggo/agent/agent.go

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -174,41 +174,53 @@ func newRegisterCmd() *cobra.Command {
174174
if registered {
175175
cfg, _ := configMgr.LoadConfig()
176176

177-
if !force {
178-
// Verify whether the agent still exists on the server.
179-
// If it is gone (e.g. deleted via dashboard or a prior uninstall),
180-
// the local config is stale and we can safely clean it up and
181-
// proceed. If the agent is still live, block as before.
182-
agentClient := api.NewClient(
183-
api.WithBaseURL(serverURL),
184-
api.WithAgentSecret(cfg.AgentSecret),
185-
)
186-
_, serverErr := agentClient.GetAgentConfig(context.Background(), cfg.AgentID)
187-
if serverErr == nil {
188-
// Agent is alive on the server – require explicit action.
189-
cmd.SilenceUsage = true
190-
if !out.IsJSON() {
191-
out.Error("Agent already registered on this machine. Run 'ggo agent unregister' first, or use --force to replace the existing registration.")
192-
}
193-
return agent.ErrAlreadyRegistered
177+
resolvedURL := serverURL
178+
if cfg != nil && cfg.ServerURL != "" {
179+
resolvedURL = cfg.ServerURL
180+
}
181+
agentClient := api.NewClient(
182+
api.WithBaseURL(resolvedURL),
183+
api.WithAgentSecret(cfg.AgentSecret),
184+
)
185+
186+
// Check if the old agent is still alive on the server.
187+
_, serverErr := agentClient.GetAgentConfig(context.Background(), cfg.AgentID)
188+
isStale := serverErr != nil
189+
190+
if isStale {
191+
if !out.IsJSON() {
192+
out.Warning(fmt.Sprintf("Stale local registration found (agent %s no longer on server). Clearing and re-registering...", cfg.AgentID))
194193
}
195-
// Agent not found on server → stale local config.
194+
} else if force {
195+
// --force: skip confirmation, auto-unregister.
196196
if !out.IsJSON() {
197-
out.Warning(fmt.Sprintf("Stale local registration found (agent %s no longer on server). Clearing config and re-registering...", cfg.AgentID))
197+
out.Info(fmt.Sprintf("Force replacing existing registration (agent %s)...", cfg.AgentID))
198198
}
199+
if deleteErr := agentClient.SelfDeleteAgent(context.Background(), cfg.AgentID); deleteErr != nil {
200+
klog.Warningf("Failed to delete old agent from server (continuing): agent_id=%s error=%v", cfg.AgentID, deleteErr)
201+
}
202+
} else if out.IsJSON() {
203+
// JSON mode (non-interactive): cannot prompt, require --force.
204+
cmd.SilenceUsage = true
205+
return agent.ErrAlreadyRegistered
199206
} else {
200-
// --force: attempt to delete the old agent from the server so it
201-
// does not linger as an orphan record.
202-
agentClient := api.NewClient(
203-
api.WithBaseURL(serverURL),
204-
api.WithAgentSecret(cfg.AgentSecret),
205-
)
207+
// Interactive: show current registration and ask for confirmation.
208+
out.Warning(fmt.Sprintf("This machine is already registered as agent %s", cfg.AgentID))
209+
confirmed, promptErr := tui.ConfirmPrompt("Unregister the existing agent and re-register with the new token?")
210+
if promptErr != nil || !confirmed {
211+
out.Info("Registration cancelled. Existing registration unchanged.")
212+
return nil
213+
}
206214
if deleteErr := agentClient.SelfDeleteAgent(context.Background(), cfg.AgentID); deleteErr != nil {
207215
klog.Warningf("Failed to delete old agent from server (continuing): agent_id=%s error=%v", cfg.AgentID, deleteErr)
216+
if !out.IsJSON() {
217+
out.Warning(fmt.Sprintf("Could not remove old agent from server: %v", deleteErr))
218+
}
219+
} else if !out.IsJSON() {
220+
out.Info(fmt.Sprintf("Old agent %s unregistered from server.", cfg.AgentID))
208221
}
209222
}
210223

211-
// Remove the stale / replaced local config before re-registering.
212224
if removeErr := configMgr.RemoveConfig(); removeErr != nil {
213225
klog.Warningf("Failed to remove local config (continuing): %v", removeErr)
214226
}

scripts/install.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ function Register-Agent {
176176
Write-Info ""
177177
Write-Info "Registering GPU Go agent..."
178178

179-
$registerArgs = @("agent", "register", "-t", $AgentToken)
179+
$registerArgs = @("agent", "register", "-t", $AgentToken, "--force")
180180

181181
if ($Endpoint) {
182182
$registerArgs += @("--server", $Endpoint)

scripts/install.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ register_agent() {
342342
# Build register command
343343
# Use sudo to ensure config is saved to root's home directory (~/.gpugo/config)
344344
# This matches the systemd service which runs as root
345-
REGISTER_CMD="${SUDO} ${binary_path} agent register -t ${token}"
345+
REGISTER_CMD="${SUDO} ${binary_path} agent register -t ${token} --force"
346346

347347
if [ -n "${ENDPOINT}" ]; then
348348
REGISTER_CMD="${REGISTER_CMD} --server ${ENDPOINT}"

0 commit comments

Comments
 (0)