From 5acf557675816f0084a33f28b6734deeb4f0fd80 Mon Sep 17 00:00:00 2001 From: Pasha Sviderski Date: Wed, 17 Dec 2025 17:36:54 +1000 Subject: [PATCH] chore: add --yes flag to 'uc machine init/add' to auto-confirm machine reset --- cmd/uncloud/machine/add.go | 7 +++++++ cmd/uncloud/machine/init.go | 16 ++++++++++++---- internal/cli/cli.go | 16 ++++++++++++++-- internal/cli/machine.go | 14 +++++++++++++- 4 files changed, 46 insertions(+), 7 deletions(-) diff --git a/cmd/uncloud/machine/add.go b/cmd/uncloud/machine/add.go index 66e59486..6f05ec4a 100644 --- a/cmd/uncloud/machine/add.go +++ b/cmd/uncloud/machine/add.go @@ -27,6 +27,7 @@ type addOptions struct { publicIP string sshKey string version string + yes bool } func NewAddCommand() *cobra.Command { @@ -41,6 +42,8 @@ Connection methods: ssh+cli://user@host - Use system SSH command (supports ProxyJump, SSH config)`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { + cli.BindEnvToFlag(cmd, "yes", "UNCLOUD_AUTO_CONFIRM") + uncli := cmd.Context().Value("cli").(*cli.CLI) // Determine if SSH CLI needs to be used and strip scheme @@ -88,6 +91,9 @@ Connection methods: &opts.version, "version", "latest", "Version of the Uncloud daemon to install on the machine.", ) + cmd.Flags().BoolVarP(&opts.yes, "yes", "y", false, + "Auto-confirm prompts (e.g., resetting an already initialised machine).\n"+ + "Should be explicitly set when running non-interactively, e.g., in CI/CD pipelines. [$UNCLOUD_AUTO_CONFIRM]") return cmd } @@ -113,6 +119,7 @@ func add(ctx context.Context, uncli *cli.CLI, remoteMachine *cli.RemoteMachine, RemoteMachine: remoteMachine, SkipInstall: opts.noInstall, Version: opts.version, + AutoConfirm: opts.yes, }) if err != nil { return err diff --git a/cmd/uncloud/machine/init.go b/cmd/uncloud/machine/init.go index 99a63a9b..54eda50c 100644 --- a/cmd/uncloud/machine/init.go +++ b/cmd/uncloud/machine/init.go @@ -28,6 +28,7 @@ type initOptions struct { sshKey string version string context string + yes bool } func NewInitCommand() *cobra.Command { @@ -56,6 +57,8 @@ Connection methods: // TODO: support initialising a cluster on the local machine. Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { + cli.BindEnvToFlag(cmd, "yes", "UNCLOUD_AUTO_CONFIRM") + uncli := cmd.Context().Value("cli").(*cli.CLI) var remoteMachine *cli.RemoteMachine @@ -82,6 +85,11 @@ Connection methods: return initCluster(cmd.Context(), uncli, remoteMachine, opts) }, } + + cmd.Flags().StringVarP( + &opts.context, "context", "c", cli.DefaultContextName, + "Name of the new context to be created in the Uncloud config to manage the cluster.", + ) cmd.Flags().StringVar(&opts.dnsEndpoint, "dns-endpoint", dns.DefaultUncloudDNSAPIEndpoint, "API endpoint for the Uncloud DNS service.") cmd.Flags().StringVarP( @@ -119,10 +127,9 @@ Connection methods: &opts.version, "version", "latest", "Version of the Uncloud daemon to install on the machine.", ) - cmd.Flags().StringVarP( - &opts.context, "context", "c", cli.DefaultContextName, - "Name of the new context to be created in the Uncloud config to manage the cluster.", - ) + cmd.Flags().BoolVarP(&opts.yes, "yes", "y", false, + "Auto-confirm prompts (e.g., resetting an already initialised machine).\n"+ + "Should be explicitly set when running non-interactively, e.g., in CI/CD pipelines. [$UNCLOUD_AUTO_CONFIRM]") return cmd } @@ -159,6 +166,7 @@ func initCluster(ctx context.Context, uncli *cli.CLI, remoteMachine *cli.RemoteM RemoteMachine: remoteMachine, SkipInstall: opts.noInstall, Version: opts.version, + AutoConfirm: opts.yes, }) if err != nil { return err diff --git a/internal/cli/cli.go b/internal/cli/cli.go index e34a393b..aa35f22a 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -162,6 +162,7 @@ type InitClusterOptions struct { RemoteMachine *RemoteMachine SkipInstall bool Version string + AutoConfirm bool } // InitCluster initialises a new cluster on a remote machine and returns a client to interact with the cluster. @@ -197,7 +198,12 @@ func (cli *CLI) initRemoteMachine(ctx context.Context, opts InitClusterOptions) return nil, fmt.Errorf("inspect machine: %w", err) } if minfo.Id != "" { - if err = promptResetMachine(ctx, machineClient.MachineClient); err != nil { + if !opts.AutoConfirm { + if err = promptResetMachine(); err != nil { + return nil, err + } + } + if err = resetAndWaitMachine(ctx, machineClient.MachineClient); err != nil { return nil, err } } @@ -297,6 +303,7 @@ type AddMachineOptions struct { RemoteMachine *RemoteMachine SkipInstall bool Version string + AutoConfirm bool } // AddMachine provisions a remote machine and adds it to the cluster. It returns a cluster client and a machine client. @@ -342,7 +349,12 @@ func (cli *CLI) AddMachine(ctx context.Context, opts AddMachineOptions) (*client return nil, nil, fmt.Errorf("machine is already a member of this cluster (%s)", minfo.Name) } - if err = promptResetMachine(ctx, machineClient.MachineClient); err != nil { + if !opts.AutoConfirm { + if err = promptResetMachine(); err != nil { + return nil, nil, err + } + } + if err = resetAndWaitMachine(ctx, machineClient.MachineClient); err != nil { return nil, nil, err } } diff --git a/internal/cli/machine.go b/internal/cli/machine.go index 8c574088..5e3383f9 100644 --- a/internal/cli/machine.go +++ b/internal/cli/machine.go @@ -2,6 +2,7 @@ package cli import ( "context" + "errors" "fmt" "os" "strings" @@ -86,7 +87,13 @@ func provisionMachine(ctx context.Context, exec sshexec.Executor, version string return nil } -func promptResetMachine(ctx context.Context, machineClient pb.MachineClient) error { +func promptResetMachine() error { + if !IsStdinTerminal() { + return errors.New("the remote machine is already initialised as a cluster member; " + + "cannot ask to confirm reset in non-interactive mode, " + + "use --yes flag or set UNCLOUD_AUTO_CONFIRM=true to auto-confirm") + } + var confirm bool form := huh.NewForm( huh.NewGroup( @@ -110,10 +117,15 @@ func promptResetMachine(ctx context.Context, machineClient pb.MachineClient) err return fmt.Errorf("remote machine is already initialised as a cluster member") } + return nil +} + +func resetAndWaitMachine(ctx context.Context, machineClient pb.MachineClient) error { if _, err := machineClient.Reset(ctx, &pb.ResetRequest{}); err != nil { return fmt.Errorf("reset remote machine: %w. You can also manually run 'uncloud-uninstall' "+ "on the remote machine to fully uninstall Uncloud from it", err) } + fmt.Println("Resetting the remote machine...") if err := waitMachineReady(ctx, machineClient, 1*time.Minute); err != nil { return fmt.Errorf("wait for machine to be ready after reset: %w", err)