Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ The following commands are available via the Smart Node client:
- `rocketpool service compose` - View the Rocket Pool service docker compose config
- `rocketpool service version, v` - View the Rocket Pool service version information
- `rocketpool service prune-eth1, n` - Shuts down the main ETH1 client and prunes its database, freeing up disk space, then restarts it when it's done.
- `rocketpool service migrate-geth` - Shuts down Geth and migrates its database from Pebble v1 to Pebble v2, then restarts it when it's done.
- `rocketpool service install-update-tracker, d` - Install the update tracker that provides the available system update count to the metrics dashboard
- `rocketpool service get-config-yaml` - Generate YAML that shows the current configuration schema, including all of the parameters and their descriptions
- `rocketpool service resync-eth1` - Deletes the main ETH1 client's chain data and resyncs it from scratch. Only use this as a last resort!
Expand Down
24 changes: 24 additions & 0 deletions rocketpool-cli/service/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,30 @@ func RegisterCommands(app *cli.Command, name string, aliases []string) {
},
},

{
Name: "migrate-geth",
Usage: "Shuts down Geth and migrates its database from Pebble v1 to Pebble v2, then restarts it when it's done.",
UsageText: "rocketpool service migrate-eth1",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "yes",
Aliases: []string{"y"},
Usage: "Automatically confirm the Geth Pebble v2 migration",
},
},
Action: func(ctx context.Context, c *cli.Command) error {

// Validate args
if err := cliutils.ValidateArgCount(c, 0); err != nil {
return err
}

// Run command
return migrateGethDatabase(c.Bool("yes"))

},
},

{
Name: "install-update-tracker",
Aliases: []string{"d"},
Expand Down
111 changes: 102 additions & 9 deletions rocketpool-cli/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,16 @@ import (

// Settings
const (
ExporterContainerSuffix string = "_exporter"
ValidatorContainerSuffix string = "_validator"
BeaconContainerSuffix string = "_eth2"
ExecutionContainerSuffix string = "_eth1"
NodeContainerSuffix string = "_node"
WatchtowerContainerSuffix string = "_watchtower"
PruneProvisionerContainerSuffix string = "_prune_provisioner"
clientDataVolumeName string = "/ethclient"
dataFolderVolumeName string = "/.rocketpool/data"
ExporterContainerSuffix string = "_exporter"
ValidatorContainerSuffix string = "_validator"
BeaconContainerSuffix string = "_eth2"
ExecutionContainerSuffix string = "_eth1"
NodeContainerSuffix string = "_node"
WatchtowerContainerSuffix string = "_watchtower"
PruneProvisionerContainerSuffix string = "_prune_provisioner"
MigrateProvisionerContainerSuffix string = "_migrate_provisioner"
clientDataVolumeName string = "/ethclient"
dataFolderVolumeName string = "/.rocketpool/data"

PruneFreeSpaceRequired uint64 = 50 * 1024 * 1024 * 1024
NethermindPruneFreeSpaceRequired uint64 = 250 * 1024 * 1024 * 1024
Expand Down Expand Up @@ -1078,6 +1079,98 @@ func pruneExecutionClient(yes bool) error {

}

// Migrates a local Geth database from Pebble v1 to Pebble v2.
func migrateGethDatabase(yes bool) error {

rp := rocketpool.NewClient()
defer rp.Close()

cfg, isNew, err := rp.LoadConfig()
if err != nil {
return err
}
if isNew {
return fmt.Errorf("Settings file not found. Please run `rocketpool service config` to set up your Smart Node.")
}

if cfg.ExecutionClientMode.Value.(cfgtypes.Mode) == cfgtypes.Mode_External {
fmt.Println("You are using an externally managed Execution client.")
fmt.Println("The Smart Node cannot migrate it. Run `geth db pebble-upgrade` against that client yourself.")
return nil
}
if cfg.IsNativeMode {
fmt.Println("You are using Native Mode.")
fmt.Println("The Smart Node cannot migrate Geth for you. Stop Geth and run `geth db pebble-upgrade --datadir <your-datadir>`.")
return nil
}

selectedEc := cfg.ExecutionClient.Value.(cfgtypes.ExecutionClient)
if selectedEc != cfgtypes.ExecutionClient_Geth {
fmt.Println("Pebble v2 migration is only implemented for Geth.")
return nil
}

fmt.Println("This will shut down Geth and migrate its database from Pebble v1 to Pebble v2.")
fmt.Println("New Geth databases already use v2; this is only needed for databases created before Geth 1.16+.")

fmt.Println("Once the migration is complete, Geth will restart automatically.")
fmt.Println()

if prompt.Declined(yes, "Are you sure you want to migrate the Geth database to Pebble v2?") {
fmt.Println("Cancelled.")
return nil
}

prefix, err := rp.GetContainerPrefix()
if err != nil {
return fmt.Errorf("Error getting container prefix: %w", err)
}
executionContainerName := prefix + ExecutionContainerSuffix

exists, err := rp.ContainerExists(executionContainerName)
if err != nil {
return fmt.Errorf("Error checking if main execution container exists: %w", err)
}
if !exists {
return fmt.Errorf("Main execution container [%s] does not exist", executionContainerName)
}

fmt.Printf("Stopping %s...\n", executionContainerName)
result, err := rp.StopContainer(executionContainerName)
if err != nil {
return fmt.Errorf("Error stopping main execution container: %w", err)
}
if result != executionContainerName {
return fmt.Errorf("Unexpected output while stopping main execution container: %s", result)
}

volume, err := rp.GetClientVolumeName(executionContainerName, clientDataVolumeName)
if err != nil {
return fmt.Errorf("Error getting execution client volume name: %w", err)
}

fmt.Printf("Provisioning Pebble v2 migration on volume %s...\n", volume)
err = rp.TouchEthclientMarker(prefix+MigrateProvisionerContainerSuffix, volume, "migrate-v2")
if err != nil {
return fmt.Errorf("Error creating migrate-v2 marker: %w", err)
}

fmt.Printf("Restarting %s...\n", executionContainerName)
result, err = rp.StartContainer(executionContainerName)
if err != nil {
return fmt.Errorf("Error starting main execution client: %w", err)
}
if result != executionContainerName {
return fmt.Errorf("Unexpected output while starting main execution client: %s", result)
}

fmt.Println()
fmt.Println("Done! Geth is now migrating to Pebble v2. Follow progress with `rocketpool service logs eth1`.")
color.YellowPrintln("Do not interrupt the client until the migration finishes.")

return nil
}

// Stops Smart Node stack containers, prunes docker, and restarts the Smart Node stack.
func resetDocker(yes, all bool, composeFiles []string) error {

Expand Down
5 changes: 5 additions & 0 deletions shared/services/rocketpool/assets/install/scripts/start-ec.sh
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ if [ "$CLIENT" = "geth" ]; then
$PERF_PREFIX /usr/local/bin/geth snapshot prune-state $GETH_NETWORK --datadir /ethclient/geth ; rm /ethclient/prune.lock
fi

elif [ -f "/ethclient/migrate-v2" ]; then
echo "Migrating Geth database from Pebble v1 to Pebble v2"
rm -f /ethclient/migrate-v2
$PERF_PREFIX /usr/local/bin/geth db pebble-upgrade $GETH_NETWORK --datadir /ethclient/geth

# Run Geth normally
else

Expand Down
10 changes: 6 additions & 4 deletions shared/services/rocketpool/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -919,21 +919,23 @@ func (c *Client) GetVolumeSize(volumeName string) (string, error) {

// Runs the prune provisioner
func (c *Client) RunPruneProvisioner(container, volume string) error {
return c.TouchEthclientMarker(container, volume, "prune.lock")
}

// Run the prune provisioner
cmd := fmt.Sprintf("docker run --rm --name %s -v %s:/ethclient alpine:latest sh -c 'touch /ethclient/prune.lock'", container, volume)
// Creates a marker file on the execution client volume (used for prune and DB migrations).
func (c *Client) TouchEthclientMarker(container, volume, marker string) error {
cmd := fmt.Sprintf("docker run --rm --name %s -v %s:/ethclient alpine:latest sh -c 'touch /ethclient/%s'", container, volume, marker)
output, err := c.readOutput(cmd)
if err != nil {
return err
}

outputString := strings.TrimSpace(string(output))
if outputString != "" {
return fmt.Errorf("Unexpected output running the prune provisioner: %s", outputString)
return fmt.Errorf("Unexpected output creating marker %s: %s", marker, outputString)
}

return nil

}

// Curls the Nethermind admin URL to trigger pruning
Expand Down
Loading