Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(daemon): add import command to download pruned snapshots #1424

Merged
merged 11 commits into from
Jul 19, 2024
177 changes: 177 additions & 0 deletions cmd/daemon/import.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package main

import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"time"

"github.com/gofrs/flock"
"github.com/pactus-project/pactus/cmd"
"github.com/pactus-project/pactus/genesis"
"github.com/pactus-project/pactus/util"
"github.com/spf13/cobra"
)

func buildImportCmd(parentCmd *cobra.Command) {
importCmd := &cobra.Command{
Use: "import",
Short: "download and import pruned data",

Check warning on line 21 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L19-L21

Added lines #L19 - L21 were not covered by tests
}
parentCmd.AddCommand(importCmd)

Check warning on line 23 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L23

Added line #L23 was not covered by tests
Ja7ad marked this conversation as resolved.
Show resolved Hide resolved

workingDirOpt := addWorkingDirOption(importCmd)
serverAddrOpt := importCmd.Flags().String("server-addr", "", "custom import server")

Check warning on line 26 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L25-L26

Added lines #L25 - L26 were not covered by tests
Ja7ad marked this conversation as resolved.
Show resolved Hide resolved

importCmd.Run = func(c *cobra.Command, _ []string) {
workingDir, err := filepath.Abs(*workingDirOpt)
cmd.FatalErrorCheck(err)

Check warning on line 30 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L28-L30

Added lines #L28 - L30 were not covered by tests

err = os.Chdir(workingDir)
cmd.FatalErrorCheck(err)

Check warning on line 33 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L32-L33

Added lines #L32 - L33 were not covered by tests

conf, gen, err := cmd.MakeConfig(workingDir)
cmd.FatalErrorCheck(err)

Check warning on line 36 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L35-L36

Added lines #L35 - L36 were not covered by tests

lockFilePath := filepath.Join(workingDir, ".pactus.lock")
fileLock := flock.New(lockFilePath)

Check warning on line 39 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L38-L39

Added lines #L38 - L39 were not covered by tests

locked, err := fileLock.TryLock()
cmd.FatalErrorCheck(err)

Check warning on line 42 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L41-L42

Added lines #L41 - L42 were not covered by tests

if !locked {
cmd.PrintWarnMsgf("Could not lock '%s', another instance is running?", lockFilePath)

Check warning on line 45 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L44-L45

Added lines #L44 - L45 were not covered by tests

return

Check warning on line 47 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L47

Added line #L47 was not covered by tests
}

storeDir, _ := filepath.Abs(conf.Store.StorePath())
if !util.IsDirNotExistsOrEmpty(storeDir) {
cmd.PrintErrorMsgf("The data directory is not empty: %s", conf.Store.StorePath())

Check warning on line 52 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L50-L52

Added lines #L50 - L52 were not covered by tests

return

Check warning on line 54 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L54

Added line #L54 was not covered by tests
}

serverAddr := cmd.SnapshotServer()

Check warning on line 57 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L57

Added line #L57 was not covered by tests

if *serverAddrOpt != "" {
serverAddr = *serverAddrOpt

Check warning on line 60 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L59-L60

Added lines #L59 - L60 were not covered by tests
}

snapshotURL := "mainnet/"

Check warning on line 63 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L63

Added line #L63 was not covered by tests

switch gen.ChainType() {
case genesis.Mainnet:
snapshotURL = serverAddr + "mainnet/"
case genesis.Testnet:
snapshotURL = serverAddr + "testnet/"
case genesis.Localnet:
cmd.PrintErrorMsgf("Unsupported chain type: %s", gen.ChainType())

Check warning on line 71 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L65-L71

Added lines #L65 - L71 were not covered by tests

return

Check warning on line 73 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L73

Added line #L73 was not covered by tests
}

metadata, err := cmd.GetSnapshotMetadata(c.Context(), snapshotURL)
if err != nil {
cmd.PrintErrorMsgf("Failed to get snapshot metadata: %s", err)

Check warning on line 78 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L76-L78

Added lines #L76 - L78 were not covered by tests

return

Check warning on line 80 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L80

Added line #L80 was not covered by tests
}

sort.Slice(metadata, func(i, j int) bool {
return i > j //nolint
Ja7ad marked this conversation as resolved.
Show resolved Hide resolved
})

Check warning on line 85 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L83-L85

Added lines #L83 - L85 were not covered by tests

cmd.PrintLine()

Check warning on line 87 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L87

Added line #L87 was not covered by tests

for i, m := range metadata {
fmt.Printf("%d. snapshot %s (%s)\n", //nolint
i+1,
parseDate(m.CreatedAt),
util.FormatBytesToHumanReadable(uint64(m.TotalSize)))

Check warning on line 93 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L90-L93

Added lines #L90 - L93 were not covered by tests
}

cmd.PrintLine()

Check warning on line 96 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L96

Added line #L96 was not covered by tests

var choice int
fmt.Printf("Please select a snapshot [1-%d]: ", len(metadata)) //nolint
Ja7ad marked this conversation as resolved.
Show resolved Hide resolved
_, err = fmt.Scanf("%d", &choice)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can use cmd.PromptInputWithRange for this.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ja7ad use cmd as it provide useful functions for interactive command lines

cmd.FatalErrorCheck(err)

Check warning on line 101 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L98-L101

Added lines #L98 - L101 were not covered by tests

if choice < 1 || choice > len(metadata) {
cmd.PrintErrorMsgf("Invalid choice.")

Check warning on line 104 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L103-L104

Added lines #L103 - L104 were not covered by tests

return

Check warning on line 106 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L106

Added line #L106 was not covered by tests
}

selected := metadata[choice-1]
tmpDir := util.TempDirPath()
extractPath := fmt.Sprintf("%s/data", tmpDir)

Check warning on line 111 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L109-L111

Added lines #L109 - L111 were not covered by tests

err = os.MkdirAll(extractPath, 0o750)
cmd.FatalErrorCheck(err)

Check warning on line 114 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L113-L114

Added lines #L113 - L114 were not covered by tests

cmd.PrintLine()

Check warning on line 116 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L116

Added line #L116 was not covered by tests

zipFileList := cmd.DownloadManager(
c.Context(),
&selected,
snapshotURL,
tmpDir,
downloadProgressBar,
)

Check warning on line 124 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L118-L124

Added lines #L118 - L124 were not covered by tests

for _, zFile := range zipFileList {
Ja7ad marked this conversation as resolved.
Show resolved Hide resolved
err := cmd.ExtractAndStoreFile(zFile, extractPath)
cmd.FatalErrorCheck(err)

Check warning on line 128 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L127-L128

Added lines #L127 - L128 were not covered by tests
}

err = os.MkdirAll(filepath.Dir(conf.Store.StorePath()), 0o750)
cmd.FatalErrorCheck(err)

Check warning on line 132 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L131-L132

Added lines #L131 - L132 were not covered by tests

err = cmd.CopyAllFiles(extractPath, conf.Store.StorePath())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better we move the whole extracted directory

cmd.FatalErrorCheck(err)

Check warning on line 135 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L134-L135

Added lines #L134 - L135 were not covered by tests

err = os.RemoveAll(tmpDir)
cmd.FatalErrorCheck(err)

Check warning on line 138 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L137-L138

Added lines #L137 - L138 were not covered by tests

_ = fileLock.Unlock()

Check warning on line 140 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L140

Added line #L140 was not covered by tests

cmd.PrintLine()
cmd.PrintLine()
cmd.PrintInfoMsgf("✅ Your node successfully imported prune data.")
cmd.PrintLine()
cmd.PrintInfoMsgf("You can start the node by running this command:")
cmd.PrintInfoMsgf("./pactus-daemon start -w %v", workingDir)

Check warning on line 147 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L142-L147

Added lines #L142 - L147 were not covered by tests
}
}

func downloadProgressBar(fileName string, totalSize, downloaded int64, percentage float64) {
barWidth := 30
completedWidth := int(float64(barWidth) * (percentage / 100))

Check warning on line 153 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L152-L153

Added lines #L152 - L153 were not covered by tests

progressBar := fmt.Sprintf(
"\r[%-*s] %3.0f%% %s (%s/ %s)",
barWidth,
strings.Repeat("=", completedWidth),
percentage,
fileName,
util.FormatBytesToHumanReadable(uint64(downloaded)),
util.FormatBytesToHumanReadable(uint64(totalSize)),
)

Check warning on line 163 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L155-L163

Added lines #L155 - L163 were not covered by tests

fmt.Print(progressBar) //nolint

Check warning on line 165 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L165

Added line #L165 was not covered by tests
}

func parseDate(dateString string) string {
const layout = "2006-01-02T15:04:05.000000"

Check warning on line 169 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L169

Added line #L169 was not covered by tests

parsedTime, err := time.Parse(layout, dateString)
if err != nil {
return ""

Check warning on line 173 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L171-L173

Added lines #L171 - L173 were not covered by tests
}

return parsedTime.Format("2006-01-02")

Check warning on line 176 in cmd/daemon/import.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/import.go#L176

Added line #L176 was not covered by tests
}
1 change: 1 addition & 0 deletions cmd/daemon/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
buildInitCmd(rootCmd)
buildStartCmd(rootCmd)
buildPruneCmd(rootCmd)
buildImportCmd(rootCmd)

Check warning on line 27 in cmd/daemon/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/daemon/main.go#L27

Added line #L27 was not covered by tests

err := rootCmd.Execute()
if err != nil {
Expand Down
Loading
Loading