github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/state/raft/storage/walwrap.go (about) 1 package storage 2 3 import ( 4 "context" 5 "io" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "sort" 10 "strings" 11 12 "github.com/coreos/etcd/raft/raftpb" 13 "github.com/coreos/etcd/wal" 14 "github.com/coreos/etcd/wal/walpb" 15 "github.com/docker/swarmkit/log" 16 "github.com/docker/swarmkit/manager/encryption" 17 "github.com/pkg/errors" 18 ) 19 20 // This package wraps the github.com/coreos/etcd/wal package, and encrypts 21 // the bytes of whatever entry is passed to it, and decrypts the bytes of 22 // whatever entry it reads. 23 24 // WAL is the interface presented by github.com/coreos/etcd/wal.WAL that we depend upon 25 type WAL interface { 26 ReadAll() ([]byte, raftpb.HardState, []raftpb.Entry, error) 27 ReleaseLockTo(index uint64) error 28 Close() error 29 Save(st raftpb.HardState, ents []raftpb.Entry) error 30 SaveSnapshot(e walpb.Snapshot) error 31 } 32 33 // WALFactory provides an interface for the different ways to get a WAL object. 34 // For instance, the etcd/wal package itself provides this 35 type WALFactory interface { 36 Create(dirpath string, metadata []byte) (WAL, error) 37 Open(dirpath string, walsnap walpb.Snapshot) (WAL, error) 38 } 39 40 var _ WAL = &wrappedWAL{} 41 var _ WAL = &wal.WAL{} 42 var _ WALFactory = walCryptor{} 43 44 // wrappedWAL wraps a github.com/coreos/etcd/wal.WAL, and handles encrypting/decrypting 45 type wrappedWAL struct { 46 *wal.WAL 47 encrypter encryption.Encrypter 48 decrypter encryption.Decrypter 49 } 50 51 // ReadAll wraps the wal.WAL.ReadAll() function, but it first checks to see if the 52 // metadata indicates that the entries are encryptd, and if so, decrypts them. 53 func (w *wrappedWAL) ReadAll() ([]byte, raftpb.HardState, []raftpb.Entry, error) { 54 metadata, state, ents, err := w.WAL.ReadAll() 55 if err != nil { 56 return metadata, state, ents, err 57 } 58 for i, ent := range ents { 59 ents[i].Data, err = encryption.Decrypt(ent.Data, w.decrypter) 60 if err != nil { 61 return nil, raftpb.HardState{}, nil, err 62 } 63 } 64 65 return metadata, state, ents, nil 66 } 67 68 // Save encrypts the entry data (if an encrypter is exists) before passing it onto the 69 // wrapped wal.WAL's Save function. 70 func (w *wrappedWAL) Save(st raftpb.HardState, ents []raftpb.Entry) error { 71 var writeEnts []raftpb.Entry 72 for _, ent := range ents { 73 data, err := encryption.Encrypt(ent.Data, w.encrypter) 74 if err != nil { 75 return err 76 } 77 writeEnts = append(writeEnts, raftpb.Entry{ 78 Index: ent.Index, 79 Term: ent.Term, 80 Type: ent.Type, 81 Data: data, 82 }) 83 } 84 85 return w.WAL.Save(st, writeEnts) 86 } 87 88 // walCryptor is an object that provides the same functions as `etcd/wal` 89 // and `etcd/snap` that we need to open a WAL object or Snapshotter object 90 type walCryptor struct { 91 encrypter encryption.Encrypter 92 decrypter encryption.Decrypter 93 } 94 95 // NewWALFactory returns an object that can be used to produce objects that 96 // will read from and write to encrypted WALs on disk. 97 func NewWALFactory(encrypter encryption.Encrypter, decrypter encryption.Decrypter) WALFactory { 98 return walCryptor{ 99 encrypter: encrypter, 100 decrypter: decrypter, 101 } 102 } 103 104 // Create returns a new WAL object with the given encrypters and decrypters. 105 func (wc walCryptor) Create(dirpath string, metadata []byte) (WAL, error) { 106 w, err := wal.Create(dirpath, metadata) 107 if err != nil { 108 return nil, err 109 } 110 return &wrappedWAL{ 111 WAL: w, 112 encrypter: wc.encrypter, 113 decrypter: wc.decrypter, 114 }, nil 115 } 116 117 // Open returns a new WAL object with the given encrypters and decrypters. 118 func (wc walCryptor) Open(dirpath string, snap walpb.Snapshot) (WAL, error) { 119 w, err := wal.Open(dirpath, snap) 120 if err != nil { 121 return nil, err 122 } 123 return &wrappedWAL{ 124 WAL: w, 125 encrypter: wc.encrypter, 126 decrypter: wc.decrypter, 127 }, nil 128 } 129 130 type originalWAL struct{} 131 132 func (o originalWAL) Create(dirpath string, metadata []byte) (WAL, error) { 133 return wal.Create(dirpath, metadata) 134 } 135 func (o originalWAL) Open(dirpath string, walsnap walpb.Snapshot) (WAL, error) { 136 return wal.Open(dirpath, walsnap) 137 } 138 139 // OriginalWAL is the original `wal` package as an implementation of the WALFactory interface 140 var OriginalWAL WALFactory = originalWAL{} 141 142 // WALData contains all the data returned by a WAL's ReadAll() function 143 // (metadata, hardwate, and entries) 144 type WALData struct { 145 Metadata []byte 146 HardState raftpb.HardState 147 Entries []raftpb.Entry 148 } 149 150 // ReadRepairWAL opens a WAL for reading, and attempts to read it. If we can't read it, attempts to repair 151 // and read again. 152 func ReadRepairWAL( 153 ctx context.Context, 154 walDir string, 155 walsnap walpb.Snapshot, 156 factory WALFactory, 157 ) (WAL, WALData, error) { 158 var ( 159 reader WAL 160 metadata []byte 161 st raftpb.HardState 162 ents []raftpb.Entry 163 err error 164 ) 165 repaired := false 166 for { 167 if reader, err = factory.Open(walDir, walsnap); err != nil { 168 return nil, WALData{}, errors.Wrap(err, "failed to open WAL") 169 } 170 if metadata, st, ents, err = reader.ReadAll(); err != nil { 171 if closeErr := reader.Close(); closeErr != nil { 172 return nil, WALData{}, closeErr 173 } 174 if _, ok := err.(encryption.ErrCannotDecrypt); ok { 175 return nil, WALData{}, errors.Wrap(err, "failed to decrypt WAL") 176 } 177 // we can only repair ErrUnexpectedEOF and we never repair twice. 178 if repaired || err != io.ErrUnexpectedEOF { 179 return nil, WALData{}, errors.Wrap(err, "irreparable WAL error") 180 } 181 if !wal.Repair(walDir) { 182 return nil, WALData{}, errors.Wrap(err, "WAL error cannot be repaired") 183 } 184 log.G(ctx).WithError(err).Info("repaired WAL error") 185 repaired = true 186 continue 187 } 188 break 189 } 190 return reader, WALData{ 191 Metadata: metadata, 192 HardState: st, 193 Entries: ents, 194 }, nil 195 } 196 197 // MigrateWALs reads existing WALs (from a particular snapshot and beyond) from one directory, encoded one way, 198 // and writes them to a new directory, encoded a different way 199 func MigrateWALs(ctx context.Context, oldDir, newDir string, oldFactory, newFactory WALFactory, snapshot walpb.Snapshot) error { 200 oldReader, waldata, err := ReadRepairWAL(ctx, oldDir, snapshot, oldFactory) 201 if err != nil { 202 return err 203 } 204 oldReader.Close() 205 206 if err := os.MkdirAll(filepath.Dir(newDir), 0700); err != nil { 207 return errors.Wrap(err, "could not create parent directory") 208 } 209 210 // keep temporary wal directory so WAL initialization appears atomic 211 tmpdirpath := filepath.Clean(newDir) + ".tmp" 212 if err := os.RemoveAll(tmpdirpath); err != nil { 213 return errors.Wrap(err, "could not remove temporary WAL directory") 214 } 215 defer os.RemoveAll(tmpdirpath) 216 217 tmpWAL, err := newFactory.Create(tmpdirpath, waldata.Metadata) 218 if err != nil { 219 return errors.Wrap(err, "could not create new WAL in temporary WAL directory") 220 } 221 defer tmpWAL.Close() 222 223 if err := tmpWAL.SaveSnapshot(snapshot); err != nil { 224 return errors.Wrap(err, "could not write WAL snapshot in temporary directory") 225 } 226 227 if err := tmpWAL.Save(waldata.HardState, waldata.Entries); err != nil { 228 return errors.Wrap(err, "could not migrate WALs to temporary directory") 229 } 230 if err := tmpWAL.Close(); err != nil { 231 return err 232 } 233 234 return os.Rename(tmpdirpath, newDir) 235 } 236 237 // ListWALs lists all the wals in a directory and returns the list in lexical 238 // order (oldest first) 239 func ListWALs(dirpath string) ([]string, error) { 240 dirents, err := ioutil.ReadDir(dirpath) 241 if err != nil { 242 return nil, err 243 } 244 245 var wals []string 246 for _, dirent := range dirents { 247 if strings.HasSuffix(dirent.Name(), ".wal") { 248 wals = append(wals, dirent.Name()) 249 } 250 } 251 252 // Sort WAL filenames in lexical order 253 sort.Sort(sort.StringSlice(wals)) 254 return wals, nil 255 }