From 528919b3170cb727b9b5bbcf1d3821a95b4f8a8e Mon Sep 17 00:00:00 2001 From: Jacob Shufro Date: Thu, 29 Aug 2024 17:46:43 -0400 Subject: [PATCH 1/3] Allow a local script to be passed to rocketpool service install --- rocketpool-cli/client/service.go | 111 +++++++++++------- rocketpool-cli/commands/service/commands.go | 1 + .../service/install-update-tracker.go | 2 +- rocketpool-cli/commands/service/install.go | 6 +- 4 files changed, 74 insertions(+), 46 deletions(-) diff --git a/rocketpool-cli/client/service.go b/rocketpool-cli/client/service.go index 4346ad080..da9471b0d 100644 --- a/rocketpool-cli/client/service.go +++ b/rocketpool-cli/client/service.go @@ -33,55 +33,44 @@ func (c *Client) downloadAndRun( url string, verbose bool, version string, - useLocalInstaller bool, extraFlags []string, ) error { var script []byte - flags := []string{ - "-v", shellescape.Quote(version), + // Download the installation script + resp, err := http.Get(fmt.Sprintf(url, version)) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("unexpected http status downloading %s script: %d", name, resp.StatusCode) } - flags = append(flags, extraFlags...) - if useLocalInstaller { - // Make sure it exists - _, err := os.Stat(name) - if errors.Is(err, os.ErrNotExist) { - return fmt.Errorf("local script [%s] does not exist", name) - } - if err != nil { - return fmt.Errorf("error checking script [%s]: %w", name, err) - } + // Sanity check that the script octet length matches content-length + script, err = io.ReadAll(resp.Body) + if err != nil { + return err + } - // Read it - script, err = os.ReadFile(name) - if err != nil { - return fmt.Errorf("error reading local script [%s]: %w", name, err) - } + if fmt.Sprint(len(script)) != resp.Header.Get("content-length") { + return fmt.Errorf("downloaded script length %d did not match content-length header %s", len(script), resp.Header.Get("content-length")) + } - // Set the "local mode" flag - flags = append(flags, "-l") - } else { - // Download the installation script - resp, err := http.Get(fmt.Sprintf(url, version)) - if err != nil { - return err - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("unexpected http status downloading %s script: %d", name, resp.StatusCode) - } + return c.runScript(script, version, verbose, extraFlags) +} - // Sanity check that the script octet length matches content-length - script, err = io.ReadAll(resp.Body) - if err != nil { - return err - } +func (c *Client) runScript( + script []byte, + version string, + verbose bool, + extraFlags []string, +) error { - if fmt.Sprint(len(script)) != resp.Header.Get("content-length") { - return fmt.Errorf("downloaded script length %d did not match content-length header %s", len(script), resp.Header.Get("content-length")) - } + flags := []string{ + "-v", shellescape.Quote(version), } + flags = append(flags, extraFlags...) // Get the escalation command escalationCmd, err := c.getEscalationCommand() @@ -134,8 +123,29 @@ func (c *Client) downloadAndRun( return nil } +func readLocalScript(path string) ([]byte, error) { + // Make sure it exists + _, err := os.Stat(path) + if errors.Is(err, os.ErrNotExist) { + return nil, fmt.Errorf("local script [%s] does not exist", path) + } + if err != nil { + return nil, fmt.Errorf("error checking script [%s]: %w", path, err) + } + + // Read it + script, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("error reading local script [%s]: %w", path, err) + } + + return script, nil +} + // Install the Rocket Pool service -func (c *Client) InstallService(verbose bool, noDeps bool, version string, path string, useLocalInstaller bool) error { +// installScriptPath is optional. If unset, the install script is downloaded from github. +func (c *Client) InstallService(verbose bool, noDeps bool, version string, path string, installScriptPath string) error { + // Get installation script flags flags := []string{} if path != "" { @@ -145,12 +155,29 @@ func (c *Client) InstallService(verbose bool, noDeps bool, version string, path flags = append(flags, "-d") } - return c.downloadAndRun(installerName, installerURL, verbose, version, useLocalInstaller, flags) + if installScriptPath != "" { + script, err := readLocalScript(installScriptPath) + if err != nil { + return err + } + // Set the "local mode" flag + flags = append(flags, "-l") + return c.runScript(script, version, verbose, flags) + } + + return c.downloadAndRun(installerName, installerURL, verbose, version, flags) } // Install the update tracker -func (c *Client) InstallUpdateTracker(verbose bool, version string, useLocalInstaller bool) error { - return c.downloadAndRun(updateTrackerInstallerName, updateTrackerURL, verbose, version, useLocalInstaller, nil) +func (c *Client) InstallUpdateTracker(verbose bool, version string, installScriptPath string) error { + if installScriptPath != "" { + script, err := readLocalScript(installScriptPath) + if err != nil { + return err + } + return c.runScript(script, version, verbose, nil) + } + return c.downloadAndRun(updateTrackerInstallerName, updateTrackerURL, verbose, version, nil) } // Start the Rocket Pool service diff --git a/rocketpool-cli/commands/service/commands.go b/rocketpool-cli/commands/service/commands.go index 6bc112ca5..f57d9745c 100644 --- a/rocketpool-cli/commands/service/commands.go +++ b/rocketpool-cli/commands/service/commands.go @@ -342,6 +342,7 @@ func RegisterCommands(app *cli.App, name string, aliases []string) { cliutils.YesFlag, installUpdateTrackerVerboseFlag, installUpdateTrackerVersionFlag, + installLocalFlag, }, Action: func(c *cli.Context) error { // Validate args diff --git a/rocketpool-cli/commands/service/install-update-tracker.go b/rocketpool-cli/commands/service/install-update-tracker.go index 73550c911..7affa87d9 100644 --- a/rocketpool-cli/commands/service/install-update-tracker.go +++ b/rocketpool-cli/commands/service/install-update-tracker.go @@ -41,7 +41,7 @@ func installUpdateTracker(c *cli.Context) error { } // Install service - err = rp.InstallUpdateTracker(c.Bool(installUpdateTrackerVerboseFlag.Name), c.String(installUpdateTrackerVersionFlag.Name), c.Bool(installLocalFlag.Name)) + err = rp.InstallUpdateTracker(c.Bool(installUpdateTrackerVerboseFlag.Name), c.String(installUpdateTrackerVersionFlag.Name), c.String(installLocalFlag.Name)) if err != nil { return err } diff --git a/rocketpool-cli/commands/service/install.go b/rocketpool-cli/commands/service/install.go index 694a4be47..680e23e62 100644 --- a/rocketpool-cli/commands/service/install.go +++ b/rocketpool-cli/commands/service/install.go @@ -37,10 +37,10 @@ var ( Aliases: []string{"u"}, Usage: "Certain configuration values are reset when the Smart Node is updated, such as Docker container tags; use this flag to force that reset, even if the Smart Node hasn't been updated", } - installLocalFlag *cli.BoolFlag = &cli.BoolFlag{ + installLocalFlag *cli.StringFlag = &cli.StringFlag{ Name: "local-script", Aliases: []string{"l"}, - Usage: fmt.Sprintf("Use a local installer script instead of pulling it down from the source repository. The script and the installer package must be in your current working directory.%sMake sure you absolutely trust the script before using this flag.%s", terminal.ColorRed, terminal.ColorReset), + Usage: fmt.Sprintf("Use a local installer script instead of pulling it down from the source repository. The script and the installer package must be in your current working directory.\n%sMake sure you absolutely trust the script before using this flag.%s", terminal.ColorRed, terminal.ColorReset), } ) @@ -67,7 +67,7 @@ func installService(c *cli.Context) error { c.Bool(installNoDepsFlag.Name), c.String(installVersionFlag.Name), c.String(installPathFlag.Name), - c.Bool(installLocalFlag.Name), + c.String(installLocalFlag.Name), ) if err != nil { return err From af5ba088f25ca15f2e774a6113325baf0976a86c Mon Sep 17 00:00:00 2001 From: Jacob Shufro Date: Thu, 29 Aug 2024 18:25:03 -0400 Subject: [PATCH 2/3] Update lint error fix for semantics --- rocketpool-daemon/common/rewards/utils.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rocketpool-daemon/common/rewards/utils.go b/rocketpool-daemon/common/rewards/utils.go index 291e9d266..b9b1f43e6 100644 --- a/rocketpool-daemon/common/rewards/utils.go +++ b/rocketpool-daemon/common/rewards/utils.go @@ -2,6 +2,7 @@ package rewards import ( "context" + "errors" "fmt" "io" "math" @@ -374,7 +375,7 @@ func DownloadRewardsFile(cfg *config.SmartNodeConfig, i *sharedtypes.IntervalInf errBuilder.WriteString(fmt.Sprintf("Downloading files with timeout %v failed.\n", timeout)) } - return fmt.Errorf("%s", errBuilder.String()) + return errors.New(errBuilder.String()) } // Gets the start slot for the given interval From 40132bbd3eeeed795dd7bb388e9c0248ccb84791 Mon Sep 17 00:00:00 2001 From: Jacob Shufro Date: Thu, 29 Aug 2024 19:37:47 -0400 Subject: [PATCH 3/3] Use paths that work on OSX and Linux --- install/deploy/templates/eth1.tmpl | 4 ++-- install/deploy/templates/eth2.tmpl | 4 ++-- install/deploy/templates/mev-boost.tmpl | 4 ++-- install/deploy/templates/validator.tmpl | 4 ++-- install/install.sh | 15 +++++++-------- install/packages/debian/debian/rules | 6 +++--- rocketpool-cli/client/compose.go | 8 ++++---- v2.md | 4 ++-- 8 files changed, 24 insertions(+), 25 deletions(-) diff --git a/install/deploy/templates/eth1.tmpl b/install/deploy/templates/eth1.tmpl index 6451c9447..1657b186d 100644 --- a/install/deploy/templates/eth1.tmpl +++ b/install/deploy/templates/eth1.tmpl @@ -16,7 +16,7 @@ services: ports: [ "{{$p2p}}:{{$p2p}}/udp", "{{$p2p}}:{{$p2p}}/tcp"{{.GetEcOpenApiPorts}} ] volumes: - {{.ExecutionClientDataVolume}}:/ethclient - - /usr/share/rocketpool/scripts:/usr/share/rocketpool/scripts:ro + - /opt/rocketpool/scripts:/opt/rocketpool/scripts:ro - /var/lib/rocketpool/data/{{.ProjectName}}:/secrets networks: - net @@ -49,7 +49,7 @@ services: - RP_GETH_ARCHIVE_MODE={{.LocalExecutionClient.Geth.ArchiveMode}} {{- end}} entrypoint: sh - command: "/usr/share/rocketpool/scripts/{{.GetEcStartScript}}" + command: "/opt/rocketpool/scripts/{{.GetEcStartScript}}" cap_drop: - all cap_add: diff --git a/install/deploy/templates/eth2.tmpl b/install/deploy/templates/eth2.tmpl index d755a2f34..d11e8182f 100644 --- a/install/deploy/templates/eth2.tmpl +++ b/install/deploy/templates/eth2.tmpl @@ -24,7 +24,7 @@ services: {{- end}} volumes: - {{.BeaconNodeDataVolume}}:/ethclient - - /usr/share/rocketpool/scripts:/usr/share/rocketpool/scripts:ro + - /opt/rocketpool/scripts:/opt/rocketpool/scripts:ro - /var/lib/rocketpool/data/{{.ProjectName}}:/secrets:ro networks: - net @@ -61,7 +61,7 @@ services: - BN_P2P_QUIC_PORT={{.LocalBeaconClient.Lighthouse.P2pQuicPort}} {{- end}} entrypoint: sh - command: "/usr/share/rocketpool/scripts/{{.GetBnStartScript}}" + command: "/opt/rocketpool/scripts/{{.GetBnStartScript}}" cap_drop: - all cap_add: diff --git a/install/deploy/templates/mev-boost.tmpl b/install/deploy/templates/mev-boost.tmpl index 113566a3c..9382606d2 100644 --- a/install/deploy/templates/mev-boost.tmpl +++ b/install/deploy/templates/mev-boost.tmpl @@ -12,7 +12,7 @@ services: restart: unless-stopped ports: [{{.GetMevBoostOpenPorts}}] volumes: - - /usr/share/rocketpool/scripts:/usr/share/rocketpool/scripts:ro + - /opt/rocketpool/scripts:/opt/rocketpool/scripts:ro networks: - net environment: @@ -20,7 +20,7 @@ services: - MEV_BOOST_PORT={{.MevBoost.Port}} - MEV_BOOST_RELAYS={{.MevBoost.GetRelayString}} entrypoint: sh - command: "/usr/share/rocketpool/scripts/{{.GetMevBoostStartScript}}" + command: "/opt/rocketpool/scripts/{{.GetMevBoostStartScript}}" cap_drop: - all cap_add: diff --git a/install/deploy/templates/validator.tmpl b/install/deploy/templates/validator.tmpl index d4fed0a8e..20913db98 100644 --- a/install/deploy/templates/validator.tmpl +++ b/install/deploy/templates/validator.tmpl @@ -13,7 +13,7 @@ services: restart: unless-stopped stop_grace_period: 3m volumes: - - /usr/share/rocketpool/scripts:/usr/share/rocketpool/scripts:ro + - /opt/rocketpool/scripts:/opt/rocketpool/scripts:ro - {{.GetValidatorsFolderPath}}:{{.GetValidatorsFolderPath}} - {{.GetAddonsFolderPath}}:{{.GetAddonsFolderPath}} networks: @@ -45,7 +45,7 @@ services: - TEKU_SHUT_DOWN_WHEN_SLASHED={{.ValidatorClient.Teku.UseSlashingProtection}} {{- end}} entrypoint: sh - command: "/usr/share/rocketpool/scripts/{{.GetVcStartScript}}" + command: "/opt/rocketpool/scripts/{{.GetVcStartScript}}" cap_drop: - all cap_add: diff --git a/install/install.sh b/install/install.sh index e97106912..244d948ba 100755 --- a/install/install.sh +++ b/install/install.sh @@ -336,12 +336,11 @@ install() { fi # Create rocket pool dir & files - RP_BIN_PATH=/usr/bin/rocketpool - RP_SHARE_PATH=/usr/share/rocketpool + RP_SYS_PATH=/opt/rocketpool RP_VAR_PATH=/var/lib/rocketpool progress 4 "Creating Rocket Pool directory structure..." - { mkdir -p "$RP_SHARE_PATH" || fail "Could not create the Rocket Pool resources directory."; } >&2 + { mkdir -p "$RP_SYS_PATH" || fail "Could not create the Rocket Pool resources directory."; } >&2 { mkdir -p "$RP_VAR_PATH/data" || fail "Could not create the Rocket Pool system data directory."; } >&2 { chmod 0700 "$RP_VAR_PATH/data" || fail "Could not set the Rocket Pool data directory permissions."; } >&2 @@ -359,11 +358,11 @@ install() { # Copy package files progress 6 "Copying package files to Rocket Pool system directory..." - { cp -r "$PACKAGE_FILES_PATH/addons" "$RP_SHARE_PATH" || fail "Could not copy addons folder to the Rocket Pool system directory."; } >&2 - { cp -r "$PACKAGE_FILES_PATH/override" "$RP_SHARE_PATH" || fail "Could not copy override folder to the Rocket Pool system directory."; } >&2 - { cp -r "$PACKAGE_FILES_PATH/scripts" "$RP_SHARE_PATH" || fail "Could not copy scripts folder to the Rocket Pool system directory."; } >&2 - { cp -r "$PACKAGE_FILES_PATH/templates" "$RP_SHARE_PATH" || fail "Could not copy templates folder to the Rocket Pool system directory."; } >&2 - { find "$RP_SHARE_PATH/scripts" -name "*.sh" -exec chmod +x {} \; 2>/dev/null || fail "Could not set executable permissions on package files."; } >&2 + { cp -r "$PACKAGE_FILES_PATH/addons" "$RP_SYS_PATH" || fail "Could not copy addons folder to the Rocket Pool system directory."; } >&2 + { cp -r "$PACKAGE_FILES_PATH/override" "$RP_SYS_PATH" || fail "Could not copy override folder to the Rocket Pool system directory."; } >&2 + { cp -r "$PACKAGE_FILES_PATH/scripts" "$RP_SYS_PATH" || fail "Could not copy scripts folder to the Rocket Pool system directory."; } >&2 + { cp -r "$PACKAGE_FILES_PATH/templates" "$RP_SYS_PATH" || fail "Could not copy templates folder to the Rocket Pool system directory."; } >&2 + { find "$RP_SYS_PATH/scripts" -name "*.sh" -exec chmod +x {} \; 2>/dev/null || fail "Could not set executable permissions on package files."; } >&2 # Clean up unnecessary files from old installations diff --git a/install/packages/debian/debian/rules b/install/packages/debian/debian/rules index 4377cd077..1c421ea7c 100755 --- a/install/packages/debian/debian/rules +++ b/install/packages/debian/debian/rules @@ -38,8 +38,8 @@ override_dh_auto_install: # Create the folder structure and copy the deploy files over install -dm 0700 debian/rocketpool/var/lib/rocketpool/data install -dm 0700 debian/rocketpool/var/lib/rocketpool/global - mkdir -p debian/rocketpool/usr/share/rocketpool/ - cp -r deploy/* debian/rocketpool/usr/share/rocketpool/ - chmod -R +x debian/rocketpool/usr/share/rocketpool/scripts + mkdir -p debian/rocketpool/opt/rocketpool/ + cp -r deploy/* debian/rocketpool/opt/rocketpool/ + chmod -R +x debian/rocketpool/opt/rocketpool/scripts override_dh_auto_test: diff --git a/rocketpool-cli/client/compose.go b/rocketpool-cli/client/compose.go index 1c1d65671..7b30b7d31 100644 --- a/rocketpool-cli/client/compose.go +++ b/rocketpool-cli/client/compose.go @@ -18,10 +18,10 @@ import ( ) const ( - templatesDir string = "/usr/share/rocketpool/templates" - addonsSourceDir string = "/usr/share/rocketpool/addons" - overrideSourceDir string = "/usr/share/rocketpool/override" - nativeScriptsSourceDir string = "/usr/share/rocketpool/scripts/native" + templatesDir string = "/opt/rocketpool/templates" + addonsSourceDir string = "/opt/rocketpool/addons" + overrideSourceDir string = "/opt/rocketpool/override" + nativeScriptsSourceDir string = "/opt/rocketpool/scripts/native" overrideDir string = "override" runtimeDir string = "runtime" extraScrapeJobsDir string = "extra-scrape-jobs" diff --git a/v2.md b/v2.md index 5552a46d3..71352ce59 100644 --- a/v2.md +++ b/v2.md @@ -17,7 +17,7 @@ NOTE: for actual installation instructions, please see the [section below](#inst - The Smart Node now comes in a `.deb` package, so users running Debian or Debian-derivative distributions can now install it via `sudo apt install rocketpool` and update it via `sudo apt update && sudo apt dist-upgrade` once the Rocket Pool repository is set up. - Packages for other distributions, such as RHEL or Fedora, will be introduced at a later date. - For now, users not on Debian-based systems can install via the traditional `rocketpool service install` command. It will still pull the necessary files from GitHub as it did before. -- Installation no longer puts all core system files into your `~/.rocketpool` directory. System files (such as Docker compose templates and execution scripts) are now put into `/usr/share/rocketpool`. The CLI binary is now installed to `/usr/bin/rocketpool`. +- Installation no longer puts all core system files into your `~/.rocketpool` directory. System files (such as Docker compose templates and execution scripts) are now put into `/opt/rocketpool`. The CLI binary is now installed to `/usr/bin/rocketpool`. - Runtime files, such as your Docker compose overrides and logging, are still put into `~/.rocketpool` by default. - Installation via the CLI can optionally now be done with local install scripts and packages on your filesystem instead of needing to reach out to GitHub. This is helpful in places where GitHub can't be accessed. - (*For developers*) The old `smartnode-install` repository has been migrated into the `smartnode` repository. @@ -264,7 +264,7 @@ Running the Smart Node is, for all practical purposes, the same as it was in `v1 - Use `rocketpool service node-logs api` to view the new API logs - Use `rocketpool service node-logs tasks` to view the node task loop logs (previously `rocketpool service logs node`) - Use `rocketpool service node-logs watchtower` to view the watchtower task loop logs (`rocketpool service logs watchtower`) -- The "non-modifiable" files like Docker compose templates and scripts are now in `/usr/share/rocketpool` instead of your user home directory (though personal files like overrides are still in your home directory) +- The "non-modifiable" files like Docker compose templates and scripts are now in `/opt/rocketpool` instead of your user home directory (though personal files like overrides are still in your home directory) - The CLI (if installed via the package manager) is now at `/usr/bin/rocketpool` instead of `~/bin/rocketpool` - Some CLI commands have moved and/or have new flags (see the overview section above) - Commands that involve selecting multiple items (such as distributing minipool balances) will now let you select arbitrary options, and submit all of the transactions at once. The overall flow will feel much faster.