github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/state/raft/storage/snapwrap.go (about)

     1  package storage
     2  
     3  import (
     4  	"io/ioutil"
     5  	"os"
     6  	"path/filepath"
     7  	"sort"
     8  	"strings"
     9  
    10  	"github.com/coreos/etcd/pkg/fileutil"
    11  	"github.com/coreos/etcd/raft/raftpb"
    12  	"github.com/coreos/etcd/snap"
    13  	"github.com/docker/swarmkit/manager/encryption"
    14  	"github.com/pkg/errors"
    15  )
    16  
    17  // This package wraps the github.com/coreos/etcd/snap package, and encrypts
    18  // the bytes of whatever snapshot is passed to it, and decrypts the bytes of
    19  // whatever snapshot it reads.
    20  
    21  // Snapshotter is the interface presented by github.com/coreos/etcd/snap.Snapshotter that we depend upon
    22  type Snapshotter interface {
    23  	SaveSnap(snapshot raftpb.Snapshot) error
    24  	Load() (*raftpb.Snapshot, error)
    25  }
    26  
    27  // SnapFactory provides an interface for the different ways to get a Snapshotter object.
    28  // For instance, the etcd/snap package itself provides this
    29  type SnapFactory interface {
    30  	New(dirpath string) Snapshotter
    31  }
    32  
    33  var _ Snapshotter = &wrappedSnap{}
    34  var _ Snapshotter = &snap.Snapshotter{}
    35  var _ SnapFactory = snapCryptor{}
    36  
    37  // wrappedSnap wraps a github.com/coreos/etcd/snap.Snapshotter, and handles
    38  // encrypting/decrypting.
    39  type wrappedSnap struct {
    40  	*snap.Snapshotter
    41  	encrypter encryption.Encrypter
    42  	decrypter encryption.Decrypter
    43  }
    44  
    45  // SaveSnap encrypts the snapshot data (if an encrypter is exists) before passing it onto the
    46  // wrapped snap.Snapshotter's SaveSnap function.
    47  func (s *wrappedSnap) SaveSnap(snapshot raftpb.Snapshot) error {
    48  	toWrite := snapshot
    49  	var err error
    50  	toWrite.Data, err = encryption.Encrypt(snapshot.Data, s.encrypter)
    51  	if err != nil {
    52  		return err
    53  	}
    54  	return s.Snapshotter.SaveSnap(toWrite)
    55  }
    56  
    57  // Load decrypts the snapshot data (if a decrypter is exists) after reading it using the
    58  // wrapped snap.Snapshotter's Load function.
    59  func (s *wrappedSnap) Load() (*raftpb.Snapshot, error) {
    60  	snapshot, err := s.Snapshotter.Load()
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  	snapshot.Data, err = encryption.Decrypt(snapshot.Data, s.decrypter)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	return snapshot, nil
    70  }
    71  
    72  // snapCryptor is an object that provides the same functions as `etcd/wal`
    73  // and `etcd/snap` that we need to open a WAL object or Snapshotter object
    74  type snapCryptor struct {
    75  	encrypter encryption.Encrypter
    76  	decrypter encryption.Decrypter
    77  }
    78  
    79  // NewSnapFactory returns a new object that can read from and write to encrypted
    80  // snapshots on disk
    81  func NewSnapFactory(encrypter encryption.Encrypter, decrypter encryption.Decrypter) SnapFactory {
    82  	return snapCryptor{
    83  		encrypter: encrypter,
    84  		decrypter: decrypter,
    85  	}
    86  }
    87  
    88  // NewSnapshotter returns a new Snapshotter with the given encrypters and decrypters
    89  func (sc snapCryptor) New(dirpath string) Snapshotter {
    90  	return &wrappedSnap{
    91  		Snapshotter: snap.New(dirpath),
    92  		encrypter:   sc.encrypter,
    93  		decrypter:   sc.decrypter,
    94  	}
    95  }
    96  
    97  type originalSnap struct{}
    98  
    99  func (o originalSnap) New(dirpath string) Snapshotter {
   100  	return snap.New(dirpath)
   101  }
   102  
   103  // OriginalSnap is the original `snap` package as an implementation of the SnapFactory interface
   104  var OriginalSnap SnapFactory = originalSnap{}
   105  
   106  // MigrateSnapshot reads the latest existing snapshot from one directory, encoded one way, and writes
   107  // it to a new directory, encoded a different way
   108  func MigrateSnapshot(oldDir, newDir string, oldFactory, newFactory SnapFactory) error {
   109  	// use temporary snapshot directory so initialization appears atomic
   110  	oldSnapshotter := oldFactory.New(oldDir)
   111  	snapshot, err := oldSnapshotter.Load()
   112  	switch err {
   113  	case snap.ErrNoSnapshot: // if there's no snapshot, the migration succeeded
   114  		return nil
   115  	case nil:
   116  		break
   117  	default:
   118  		return err
   119  	}
   120  
   121  	tmpdirpath := filepath.Clean(newDir) + ".tmp"
   122  	if fileutil.Exist(tmpdirpath) {
   123  		if err := os.RemoveAll(tmpdirpath); err != nil {
   124  			return errors.Wrap(err, "could not remove temporary snapshot directory")
   125  		}
   126  	}
   127  	if err := fileutil.CreateDirAll(tmpdirpath); err != nil {
   128  		return errors.Wrap(err, "could not create temporary snapshot directory")
   129  	}
   130  	tmpSnapshotter := newFactory.New(tmpdirpath)
   131  
   132  	// write the new snapshot to the temporary location
   133  	if err = tmpSnapshotter.SaveSnap(*snapshot); err != nil {
   134  		return err
   135  	}
   136  
   137  	return os.Rename(tmpdirpath, newDir)
   138  }
   139  
   140  // ListSnapshots lists all the snapshot files in a particular directory and returns
   141  // the snapshot files in reverse lexical order (newest first)
   142  func ListSnapshots(dirpath string) ([]string, error) {
   143  	dirents, err := ioutil.ReadDir(dirpath)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  
   148  	var snapshots []string
   149  	for _, dirent := range dirents {
   150  		if strings.HasSuffix(dirent.Name(), ".snap") {
   151  			snapshots = append(snapshots, dirent.Name())
   152  		}
   153  	}
   154  
   155  	// Sort snapshot filenames in reverse lexical order
   156  	sort.Sort(sort.Reverse(sort.StringSlice(snapshots)))
   157  	return snapshots, nil
   158  }