github.com/palisadeinc/bor@v0.0.0-20230615125219-ab7196213d15/internal/cli/removedb.go (about)

     1  package cli
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/ethereum/go-ethereum/common"
    11  	"github.com/ethereum/go-ethereum/internal/cli/flagset"
    12  	"github.com/ethereum/go-ethereum/internal/cli/server"
    13  	"github.com/ethereum/go-ethereum/log"
    14  	"github.com/ethereum/go-ethereum/node"
    15  
    16  	"github.com/mitchellh/cli"
    17  )
    18  
    19  // RemoveDBCommand is for removing blockchain and state databases
    20  type RemoveDBCommand struct {
    21  	*Meta2
    22  
    23  	datadir string
    24  }
    25  
    26  const (
    27  	chaindataPath        string = "chaindata"
    28  	ancientPath          string = "ancient"
    29  	trieCacheJournalPath string = "triecache"
    30  	lightchaindataPath   string = "lightchaindata"
    31  )
    32  
    33  // MarkDown implements cli.MarkDown interface
    34  func (c *RemoveDBCommand) MarkDown() string {
    35  	items := []string{
    36  		"# RemoveDB",
    37  		"The ```bor removedb``` command will remove the blockchain and state databases at the given datadir location",
    38  		c.Flags().MarkDown(),
    39  	}
    40  
    41  	return strings.Join(items, "\n\n")
    42  }
    43  
    44  // Help implements the cli.Command interface
    45  func (c *RemoveDBCommand) Help() string {
    46  	return `Usage: bor removedb <datadir>
    47  
    48    This command will remove the blockchain and state databases at the given datadir location`
    49  }
    50  
    51  // Synopsis implements the cli.Command interface
    52  func (c *RemoveDBCommand) Synopsis() string {
    53  	return "Remove blockchain and state databases"
    54  }
    55  
    56  func (c *RemoveDBCommand) Flags() *flagset.Flagset {
    57  	flags := c.NewFlagSet("removedb")
    58  
    59  	flags.StringFlag(&flagset.StringFlag{
    60  		Name:  "datadir",
    61  		Value: &c.datadir,
    62  		Usage: "Path of the data directory to store information",
    63  	})
    64  
    65  	return flags
    66  }
    67  
    68  // Run implements the cli.Command interface
    69  func (c *RemoveDBCommand) Run(args []string) int {
    70  	flags := c.Flags()
    71  
    72  	// parse datadir
    73  	if err := flags.Parse(args); err != nil {
    74  		c.UI.Error(err.Error())
    75  		return 1
    76  	}
    77  
    78  	datadir := c.datadir
    79  	if datadir == "" {
    80  		datadir = server.DefaultDataDir()
    81  	}
    82  
    83  	// create ethereum node config with just the datadir
    84  	nodeCfg := &node.Config{DataDir: datadir}
    85  
    86  	// Remove the full node state database
    87  	path := nodeCfg.ResolvePath(chaindataPath)
    88  	if common.FileExist(path) {
    89  		confirmAndRemoveDB(c.UI, path, "full node state database")
    90  	} else {
    91  		log.Info("Full node state database missing", "path", path)
    92  	}
    93  
    94  	// Remove the full node ancient database
    95  	// Note: The old cli used DatabaseFreezer path from config if provided explicitly
    96  	// We don't have access to eth config and hence we assume it to be
    97  	// under the "chaindata" folder.
    98  	path = filepath.Join(nodeCfg.ResolvePath(chaindataPath), ancientPath)
    99  	if common.FileExist(path) {
   100  		confirmAndRemoveDB(c.UI, path, "full node ancient database")
   101  	} else {
   102  		log.Info("Full node ancient database missing", "path", path)
   103  	}
   104  
   105  	// Remove the light node database
   106  	path = nodeCfg.ResolvePath(lightchaindataPath)
   107  	if common.FileExist(path) {
   108  		confirmAndRemoveDB(c.UI, path, "light node database")
   109  	} else {
   110  		log.Info("Light node database missing", "path", path)
   111  	}
   112  
   113  	return 0
   114  }
   115  
   116  // confirmAndRemoveDB prompts the user for a last confirmation and removes the
   117  // folder if accepted.
   118  func confirmAndRemoveDB(ui cli.Ui, database string, kind string) {
   119  	for {
   120  		confirm, err := ui.Ask(fmt.Sprintf("Remove %s (%s)? [y/n]", kind, database))
   121  
   122  		switch {
   123  		case err != nil:
   124  			ui.Output(err.Error())
   125  			return
   126  		case confirm != "":
   127  			switch strings.ToLower(confirm) {
   128  			case "y":
   129  				start := time.Now()
   130  				err = filepath.Walk(database, func(path string, info os.FileInfo, err error) error {
   131  					// If we're at the top level folder, recurse into
   132  					if path == database {
   133  						return nil
   134  					}
   135  					// Delete all the files, but not subfolders
   136  					if !info.IsDir() {
   137  						return os.Remove(path)
   138  					}
   139  					return filepath.SkipDir
   140  				})
   141  
   142  				if err != nil && err != filepath.SkipDir {
   143  					ui.Output(err.Error())
   144  				} else {
   145  					log.Info("Database successfully deleted", "path", database, "elapsed", common.PrettyDuration(time.Since(start)))
   146  				}
   147  
   148  				return
   149  			case "n":
   150  				log.Info("Database deletion skipped", "path", database)
   151  				return
   152  			}
   153  		}
   154  	}
   155  }