github.com/cosmos/cosmos-sdk@v0.50.10/client/snapshot/load.go (about)

     1  package snapshot
     2  
     3  import (
     4  	"archive/tar"
     5  	"bytes"
     6  	"compress/gzip"
     7  	"fmt"
     8  	"io"
     9  	"os"
    10  	"reflect"
    11  	"strconv"
    12  
    13  	"github.com/spf13/cobra"
    14  
    15  	snapshottypes "cosmossdk.io/store/snapshots/types"
    16  
    17  	"github.com/cosmos/cosmos-sdk/server"
    18  )
    19  
    20  const SnapshotFileName = "_snapshot"
    21  
    22  // LoadArchiveCmd load a portable archive format snapshot into snapshot store
    23  func LoadArchiveCmd() *cobra.Command {
    24  	return &cobra.Command{
    25  		Use:   "load <archive-file>",
    26  		Short: "Load a snapshot archive file (.tar.gz) into snapshot store",
    27  		Args:  cobra.ExactArgs(1),
    28  		RunE: func(cmd *cobra.Command, args []string) error {
    29  			ctx := server.GetServerContextFromCmd(cmd)
    30  			snapshotStore, err := server.GetSnapshotStore(ctx.Viper)
    31  			if err != nil {
    32  				return err
    33  			}
    34  
    35  			path := args[0]
    36  			fp, err := os.Open(path)
    37  			if err != nil {
    38  				return fmt.Errorf("failed to open archive file: %w", err)
    39  			}
    40  			reader, err := gzip.NewReader(fp)
    41  			if err != nil {
    42  				return fmt.Errorf("failed to create gzip reader: %w", err)
    43  			}
    44  
    45  			var snapshot snapshottypes.Snapshot
    46  			tr := tar.NewReader(reader)
    47  			if err != nil {
    48  				return fmt.Errorf("failed to create tar reader: %w", err)
    49  			}
    50  
    51  			hdr, err := tr.Next()
    52  			if err != nil {
    53  				return fmt.Errorf("failed to read snapshot file header: %w", err)
    54  			}
    55  			if hdr.Name != SnapshotFileName {
    56  				return fmt.Errorf("invalid archive, expect file: snapshot, got: %s", hdr.Name)
    57  			}
    58  			bz, err := io.ReadAll(tr)
    59  			if err != nil {
    60  				return fmt.Errorf("failed to read snapshot file: %w", err)
    61  			}
    62  			if err := snapshot.Unmarshal(bz); err != nil {
    63  				return fmt.Errorf("failed to unmarshal snapshot: %w", err)
    64  			}
    65  
    66  			// make sure the channel is unbuffered, because the tar reader can't do concurrency
    67  			chunks := make(chan io.ReadCloser)
    68  			quitChan := make(chan *snapshottypes.Snapshot)
    69  			go func() {
    70  				defer close(quitChan)
    71  
    72  				savedSnapshot, err := snapshotStore.Save(snapshot.Height, snapshot.Format, chunks)
    73  				if err != nil {
    74  					cmd.Println("failed to save snapshot", err)
    75  					return
    76  				}
    77  				quitChan <- savedSnapshot
    78  			}()
    79  
    80  			for i := uint32(0); i < snapshot.Chunks; i++ {
    81  				hdr, err = tr.Next()
    82  				if err != nil {
    83  					if err == io.EOF {
    84  						break
    85  					}
    86  					return err
    87  				}
    88  
    89  				if hdr.Name != strconv.FormatInt(int64(i), 10) {
    90  					return fmt.Errorf("invalid archive, expect file: %d, got: %s", i, hdr.Name)
    91  				}
    92  
    93  				bz, err := io.ReadAll(tr)
    94  				if err != nil {
    95  					return fmt.Errorf("failed to read chunk file: %w", err)
    96  				}
    97  				chunks <- io.NopCloser(bytes.NewReader(bz))
    98  			}
    99  			close(chunks)
   100  
   101  			savedSnapshot := <-quitChan
   102  			if savedSnapshot == nil {
   103  				return fmt.Errorf("failed to save snapshot")
   104  			}
   105  
   106  			if !reflect.DeepEqual(&snapshot, savedSnapshot) {
   107  				_ = snapshotStore.Delete(snapshot.Height, snapshot.Format)
   108  				return fmt.Errorf("invalid archive, the saved snapshot is not equal to the original one")
   109  			}
   110  
   111  			return nil
   112  		},
   113  	}
   114  }