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 }