From 74e75aec133e408cf79dda2b44eca4e7a1fdb375 Mon Sep 17 00:00:00 2001 From: Michael Feher Date: Wed, 11 Dec 2024 21:43:13 -0500 Subject: [PATCH] chore(node): call sudo on demand --- cmd/node/install.go | 10 ++++++ cmd/node/node.go | 11 ------ internal/algod/linux/linux.go | 63 +++++++++++++++++++++++------------ 3 files changed, 52 insertions(+), 32 deletions(-) diff --git a/cmd/node/install.go b/cmd/node/install.go index e66c0217..61a68bf1 100644 --- a/cmd/node/install.go +++ b/cmd/node/install.go @@ -36,6 +36,16 @@ var installCmd = &cobra.Command{ log.Error(err) os.Exit(1) } + + // If it's not running, start the daemon (can happen) + if !algod.IsRunning() { + err = algod.Start() + if err != nil { + log.Error(err) + os.Exit(1) + } + } + log.Info(style.Green.Render("Algorand installed successfully 🎉")) }, } diff --git a/cmd/node/node.go b/cmd/node/node.go index 96eb36e3..1fca2ecd 100644 --- a/cmd/node/node.go +++ b/cmd/node/node.go @@ -1,13 +1,10 @@ package node import ( - "errors" "github.com/algorandfoundation/algorun-tui/internal/algod" "github.com/algorandfoundation/algorun-tui/ui/style" "github.com/charmbracelet/log" "github.com/spf13/cobra" - "os" - "runtime" ) const SudoWarningMsg = "(You may be prompted for your password)" @@ -23,14 +20,6 @@ var Cmd = &cobra.Command{ Use: "node", Short: "Node Management", Long: style.Purple(style.BANNER) + "\n" + style.LightBlue("Manage your Algorand node"), - PersistentPreRunE: func(cmd *cobra.Command, args []string) error { - - // Check that we are calling with sudo on linux - if os.Geteuid() != 0 && runtime.GOOS == "linux" { - return errors.New(PermissionErrorMsg) - } - return nil - }, } func NeedsToBeRunning(cmd *cobra.Command, args []string) { diff --git a/internal/algod/linux/linux.go b/internal/algod/linux/linux.go index 83ceed9c..eb5f5b29 100644 --- a/internal/algod/linux/linux.go +++ b/internal/algod/linux/linux.go @@ -6,9 +6,9 @@ import ( "github.com/algorandfoundation/algorun-tui/internal/algod/fallback" "github.com/algorandfoundation/algorun-tui/internal/system" "github.com/charmbracelet/log" - "os" "os/exec" + "runtime" "strings" "text/template" ) @@ -21,25 +21,46 @@ type Algod struct { DataDirectoryPath string } +// InstallSudoCmds generates installation commands for "sudo" based on the detected package manager and system state. +func InstallSudoCmds() system.CmdsList { + var cmds system.CmdsList + if system.CmdExists("sudo") || os.Geteuid() != 0 { + return cmds + } + if system.CmdExists("apt-get") { + return system.CmdsList{ + {"apt-get", "update"}, + {"apt-get", "install", "-y", "sudo"}, + } + } + + if system.CmdExists("dnf") { + return system.CmdsList{ + {"dnf", "install", "-y", "sudo"}, + } + } + return cmds +} + // Install installs Algorand development tools or node software depending on the package manager. func Install() error { log.Info("Installing Algod on Linux") // Based off of https://developer.algorand.org/docs/run-a-node/setup/install/#installation-with-a-package-manager if system.CmdExists("apt-get") { // On some Debian systems we use apt-get log.Info("Installing with apt-get") - return system.RunAll(system.CmdsList{ - {"apt-get", "update"}, - {"apt-get", "install", "-y", "gnupg2", "curl", "software-properties-common"}, - {"sh", "-c", "curl -o - https://releases.algorand.com/key.pub | tee /etc/apt/trusted.gpg.d/algorand.asc"}, - {"sh", "-c", `add-apt-repository -y "deb [arch=amd64] https://releases.algorand.com/deb/ stable main"`}, - {"apt-get", "update"}, - {"apt-get", "install", "-y", "algorand-devtools"}, - }) + return system.RunAll(append(InstallSudoCmds(), system.CmdsList{ + {"sudo", "apt-get", "update"}, + {"sudo", "apt-get", "install", "-y", "gnupg2", "curl", "software-properties-common"}, + {"sh", "-c", "curl -o - https://releases.algorand.com/key.pub | sudo tee /etc/apt/trusted.gpg.d/algorand.asc"}, + {"sudo", "add-apt-repository", "-y", fmt.Sprintf("deb [arch=%s] https://releases.algorand.com/deb/ stable main", runtime.GOARCH)}, + {"sudo", "apt-get", "update"}, + {"sudo", "apt-get", "install", "-y", "algorand-devtools"}, + }...)) } if system.CmdExists("dnf") { // On Fedora and CentOs8 there's the dnf package manager log.Printf("Installing with dnf") - return system.RunAll(system.CmdsList{ + return system.RunAll(append(InstallSudoCmds(), system.CmdsList{ {"curl", "-O", "https://releases.algorand.com/rpm/rpm_algorand.pub"}, {"rpmkeys", "--import", "rpm_algorand.pub"}, {"dnf", "install", "-y", "dnf-command(config-manager)"}, @@ -48,7 +69,7 @@ func Install() error { {"systemctl", "enable", "algorand.service"}, {"systemctl", "start", "algorand.service"}, {"rm", "-f", "rpm_algorand.pub"}, - }) + }...)) } @@ -65,14 +86,14 @@ func Uninstall() error { if system.CmdExists("apt-get") { log.Info("Using apt-get package manager") unInstallCmds = [][]string{ - {"apt-get", "autoremove", "algorand-devtools", "algorand", "-y"}, + {"sudo", "apt-get", "autoremove", "algorand-devtools", "algorand", "-y"}, } } // On Fedora and CentOs8 there's the dnf package manager if system.CmdExists("dnf") { log.Info("Using dnf package manager") unInstallCmds = [][]string{ - {"dnf", "remove", "algorand-devtools", "algorand", "-y"}, + {"sudo", "dnf", "remove", "algorand-devtools", "algorand", "-y"}, } } // Error on unsupported package managers @@ -81,8 +102,8 @@ func Uninstall() error { } // Commands to clear systemd algorand.service and any other files, like the configuration override - unInstallCmds = append(unInstallCmds, []string{"bash", "-c", "rm -rf /etc/systemd/system/algorand*"}) - unInstallCmds = append(unInstallCmds, []string{"systemctl", "daemon-reload"}) + unInstallCmds = append(unInstallCmds, []string{"sudo", "bash", "-c", "rm -rf /etc/systemd/system/algorand*"}) + unInstallCmds = append(unInstallCmds, []string{"sudo", "systemctl", "daemon-reload"}) return system.RunAll(unInstallCmds) } @@ -92,13 +113,13 @@ func Uninstall() error { func Upgrade() error { if system.CmdExists("apt-get") { return system.RunAll(system.CmdsList{ - {"apt-get", "update"}, - {"apt-get", "install", "--only-upgrade", "-y", "algorand-devtools", "algorand"}, + {"sudo", "apt-get", "update"}, + {"sudo", "apt-get", "install", "--only-upgrade", "-y", "algorand-devtools", "algorand"}, }) } if system.CmdExists("dnf") { return system.RunAll(system.CmdsList{ - {"dnf", "update", "-y", "--refresh", "algorand-devtools", "algorand"}, + {"sudo", "dnf", "update", "-y", "--refresh", "algorand-devtools", "algorand"}, }) } return fmt.Errorf("the *node upgrade* command is currently only available for installations done with an approved package manager. Please use a different method to upgrade") @@ -109,21 +130,21 @@ func Upgrade() error { // Returns an error if the command fails. // TODO: Replace with D-Bus integration func Start() error { - return exec.Command("systemctl", "start", "algorand").Run() + return exec.Command("sudo", "systemctl", "start", "algorand").Run() } // Stop shuts down the Algorand algod system process on Linux using the systemctl stop command. // Returns an error if the operation fails. // TODO: Replace with D-Bus integration func Stop() error { - return exec.Command("systemctl", "stop", "algorand").Run() + return exec.Command("sudo", "systemctl", "stop", "algorand").Run() } // IsService checks if the "algorand.service" is listed as a systemd unit file on Linux. // Returns true if it exists. // TODO: Replace with D-Bus integration func IsService() bool { - out, err := system.Run([]string{"systemctl", "list-unit-files", "algorand.service"}) + out, err := system.Run([]string{"sudo", "systemctl", "list-unit-files", "algorand.service"}) if err != nil { return false }