github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/state/raft/storage/walwrap_test.go (about) 1 package storage 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 "testing" 11 12 "github.com/coreos/etcd/raft/raftpb" 13 "github.com/coreos/etcd/wal/walpb" 14 "github.com/docker/swarmkit/api" 15 "github.com/docker/swarmkit/manager/encryption" 16 "github.com/stretchr/testify/require" 17 ) 18 19 var _ WALFactory = walCryptor{} 20 21 // Generates a bunch of WAL test data 22 func makeWALData(index uint64, term uint64) ([]byte, []raftpb.Entry, walpb.Snapshot) { 23 wsn := walpb.Snapshot{ 24 Index: index, 25 Term: term, 26 } 27 28 var entries []raftpb.Entry 29 for i := wsn.Index + 1; i < wsn.Index+6; i++ { 30 entries = append(entries, raftpb.Entry{ 31 Term: wsn.Term + 1, 32 Index: i, 33 Data: []byte(fmt.Sprintf("Entry %d", i)), 34 }) 35 } 36 37 return []byte("metadata"), entries, wsn 38 } 39 40 func createWithWAL(t *testing.T, w WALFactory, metadata []byte, startSnap walpb.Snapshot, entries []raftpb.Entry) string { 41 walDir, err := ioutil.TempDir("", "waltests") 42 require.NoError(t, err) 43 require.NoError(t, os.RemoveAll(walDir)) 44 45 walWriter, err := w.Create(walDir, metadata) 46 require.NoError(t, err) 47 48 require.NoError(t, walWriter.SaveSnapshot(startSnap)) 49 require.NoError(t, walWriter.Save(raftpb.HardState{}, entries)) 50 require.NoError(t, walWriter.Close()) 51 52 return walDir 53 } 54 55 // WAL can read entries are not wrapped, but not encrypted 56 func TestReadAllWrappedNoEncryption(t *testing.T) { 57 metadata, entries, snapshot := makeWALData(1, 1) 58 wrappedEntries := make([]raftpb.Entry, len(entries)) 59 for i, entry := range entries { 60 r := api.MaybeEncryptedRecord{Data: entry.Data} 61 data, err := r.Marshal() 62 require.NoError(t, err) 63 entry.Data = data 64 wrappedEntries[i] = entry 65 } 66 67 tempdir := createWithWAL(t, OriginalWAL, metadata, snapshot, wrappedEntries) 68 defer os.RemoveAll(tempdir) 69 70 c := NewWALFactory(encryption.NoopCrypter, encryption.NoopCrypter) 71 wrapped, err := c.Open(tempdir, snapshot) 72 require.NoError(t, err) 73 defer wrapped.Close() 74 75 metaW, _, entsW, err := wrapped.ReadAll() 76 require.NoError(t, err) 77 require.NoError(t, wrapped.Close()) 78 79 require.Equal(t, metadata, metaW) 80 require.Equal(t, entries, entsW) 81 } 82 83 // When reading WAL, if the decrypter can't read the encryption type, errors 84 func TestReadAllNoSupportedDecrypter(t *testing.T) { 85 metadata, entries, snapshot := makeWALData(1, 1) 86 for i, entry := range entries { 87 r := api.MaybeEncryptedRecord{Data: entry.Data, Algorithm: api.MaybeEncryptedRecord_Algorithm(-3)} 88 data, err := r.Marshal() 89 require.NoError(t, err) 90 entries[i].Data = data 91 } 92 93 tempdir := createWithWAL(t, OriginalWAL, metadata, snapshot, entries) 94 defer os.RemoveAll(tempdir) 95 96 c := NewWALFactory(encryption.NoopCrypter, encryption.NoopCrypter) 97 wrapped, err := c.Open(tempdir, snapshot) 98 require.NoError(t, err) 99 defer wrapped.Close() 100 101 _, _, _, err = wrapped.ReadAll() 102 require.Error(t, err) 103 defer wrapped.Close() 104 } 105 106 // When reading WAL, if a decrypter is available for the encryption type but any 107 // entry is incorrectly encryptd, an error is returned 108 func TestReadAllEntryIncorrectlyEncrypted(t *testing.T) { 109 crypter := &meowCrypter{} 110 metadata, entries, snapshot := makeWALData(1, 1) 111 112 // metadata is correctly encryptd, but entries are not meow-encryptd 113 for i, entry := range entries { 114 r := api.MaybeEncryptedRecord{Data: entry.Data, Algorithm: crypter.Algorithm()} 115 data, err := r.Marshal() 116 require.NoError(t, err) 117 entries[i].Data = data 118 } 119 120 tempdir := createWithWAL(t, OriginalWAL, metadata, snapshot, entries) 121 defer os.RemoveAll(tempdir) 122 123 c := NewWALFactory(encryption.NoopCrypter, crypter) 124 wrapped, err := c.Open(tempdir, snapshot) 125 require.NoError(t, err) 126 127 _, _, _, err = wrapped.ReadAll() 128 require.Error(t, err) 129 require.Contains(t, err.Error(), "not meowcoded") 130 require.NoError(t, wrapped.Close()) 131 } 132 133 // The entry data and metadata are encryptd with the given encrypter, and a regular 134 // WAL will see them as such. 135 func TestSave(t *testing.T) { 136 metadata, entries, snapshot := makeWALData(1, 1) 137 138 crypter := &meowCrypter{} 139 c := NewWALFactory(crypter, encryption.NoopCrypter) 140 tempdir := createWithWAL(t, c, metadata, snapshot, entries) 141 defer os.RemoveAll(tempdir) 142 143 ogWAL, err := OriginalWAL.Open(tempdir, snapshot) 144 require.NoError(t, err) 145 defer ogWAL.Close() 146 147 meta, state, ents, err := ogWAL.ReadAll() 148 require.NoError(t, err) 149 require.Equal(t, metadata, meta) 150 require.Equal(t, state, state) 151 for _, ent := range ents { 152 var encrypted api.MaybeEncryptedRecord 153 require.NoError(t, encrypted.Unmarshal(ent.Data)) 154 155 require.Equal(t, crypter.Algorithm(), encrypted.Algorithm) 156 require.True(t, bytes.HasSuffix(encrypted.Data, []byte("🐱"))) 157 } 158 } 159 160 // If encryption fails, saving will fail 161 func TestSaveEncryptionFails(t *testing.T) { 162 metadata, entries, snapshot := makeWALData(1, 1) 163 164 tempdir, err := ioutil.TempDir("", "waltests") 165 require.NoError(t, err) 166 os.RemoveAll(tempdir) 167 defer os.RemoveAll(tempdir) 168 169 // fail encrypting one of the entries, but not the first one 170 c := NewWALFactory(&meowCrypter{encryptFailures: map[string]struct{}{ 171 "Entry 3": {}, 172 }}, nil) 173 wrapped, err := c.Create(tempdir, metadata) 174 require.NoError(t, err) 175 176 require.NoError(t, wrapped.SaveSnapshot(snapshot)) 177 err = wrapped.Save(raftpb.HardState{}, entries) 178 require.Error(t, err) 179 require.Contains(t, err.Error(), "refusing to encrypt") 180 require.NoError(t, wrapped.Close()) 181 182 // no entries are written at all 183 ogWAL, err := OriginalWAL.Open(tempdir, snapshot) 184 require.NoError(t, err) 185 defer ogWAL.Close() 186 187 _, _, ents, err := ogWAL.ReadAll() 188 require.NoError(t, err) 189 require.Empty(t, ents) 190 } 191 192 // If the underlying WAL returns an error when opening or creating, the error 193 // is propagated up. 194 func TestCreateOpenInvalidDirFails(t *testing.T) { 195 c := NewWALFactory(encryption.NoopCrypter, encryption.NoopCrypter) 196 197 _, err := c.Create("/not/existing/directory", []byte("metadata")) 198 require.Error(t, err) 199 200 tempDir, err := ioutil.TempDir("", "test-migrate") 201 require.NoError(t, err) 202 defer os.RemoveAll(tempDir) 203 204 _, err = c.Open(tempDir, walpb.Snapshot{}) // invalid because no WAL file 205 require.Error(t, err) 206 } 207 208 // A WAL can read what it wrote so long as it has a corresponding decrypter 209 func TestSaveAndRead(t *testing.T) { 210 crypter := &meowCrypter{} 211 metadata, entries, snapshot := makeWALData(1, 1) 212 213 c := NewWALFactory(crypter, crypter) 214 tempdir := createWithWAL(t, c, metadata, snapshot, entries) 215 defer os.RemoveAll(tempdir) 216 217 wrapped, err := c.Open(tempdir, snapshot) 218 require.NoError(t, err) 219 220 meta, _, ents, err := wrapped.ReadAll() 221 require.NoError(t, wrapped.Close()) 222 require.NoError(t, err) 223 require.Equal(t, metadata, meta) 224 require.Equal(t, entries, ents) 225 } 226 227 func TestReadRepairWAL(t *testing.T) { 228 metadata, entries, snapshot := makeWALData(1, 1) 229 tempdir := createWithWAL(t, OriginalWAL, metadata, snapshot, entries) 230 defer os.RemoveAll(tempdir) 231 232 // there should only be one WAL file in there - corrupt it 233 files, err := ioutil.ReadDir(tempdir) 234 require.NoError(t, err) 235 require.Len(t, files, 1) 236 237 fName := filepath.Join(tempdir, files[0].Name()) 238 fileContents, err := ioutil.ReadFile(fName) 239 require.NoError(t, err) 240 require.NoError(t, ioutil.WriteFile(fName, fileContents[:200], files[0].Mode())) 241 242 ogWAL, err := OriginalWAL.Open(tempdir, snapshot) 243 require.NoError(t, err) 244 _, _, _, err = ogWAL.ReadAll() 245 require.Error(t, err) 246 require.NoError(t, ogWAL.Close()) 247 248 ogWAL, waldata, err := ReadRepairWAL(context.Background(), tempdir, snapshot, OriginalWAL) 249 require.NoError(t, err) 250 require.Equal(t, metadata, waldata.Metadata) 251 require.NoError(t, ogWAL.Close()) 252 } 253 254 func TestMigrateWALs(t *testing.T) { 255 metadata, entries, snapshot := makeWALData(1, 1) 256 coder := &meowCrypter{} 257 c := NewWALFactory(coder, coder) 258 259 var ( 260 err error 261 dirs = make([]string, 2) 262 ) 263 264 tempDir, err := ioutil.TempDir("", "test-migrate") 265 require.NoError(t, err) 266 defer os.RemoveAll(tempDir) 267 268 for i := range dirs { 269 dirs[i] = filepath.Join(tempDir, fmt.Sprintf("walDir%d", i)) 270 } 271 272 origDir := createWithWAL(t, OriginalWAL, metadata, snapshot, entries) 273 defer os.RemoveAll(origDir) 274 275 // original to new 276 oldDir := origDir 277 newDir := dirs[0] 278 279 err = MigrateWALs(context.Background(), oldDir, newDir, OriginalWAL, c, snapshot) 280 require.NoError(t, err) 281 282 newWAL, err := c.Open(newDir, snapshot) 283 require.NoError(t, err) 284 meta, _, ents, err := newWAL.ReadAll() 285 require.NoError(t, err) 286 require.Equal(t, metadata, meta) 287 require.Equal(t, entries, ents) 288 require.NoError(t, newWAL.Close()) 289 290 // new to original 291 oldDir = dirs[0] 292 newDir = dirs[1] 293 294 err = MigrateWALs(context.Background(), oldDir, newDir, c, OriginalWAL, snapshot) 295 require.NoError(t, err) 296 297 newWAL, err = OriginalWAL.Open(newDir, snapshot) 298 require.NoError(t, err) 299 meta, _, ents, err = newWAL.ReadAll() 300 require.NoError(t, err) 301 require.Equal(t, metadata, meta) 302 require.Equal(t, entries, ents) 303 require.NoError(t, newWAL.Close()) 304 305 // If we can't read the old directory (for instance if it doesn't exist), a temp directory 306 // is not created 307 for _, dir := range dirs { 308 require.NoError(t, os.RemoveAll(dir)) 309 } 310 oldDir = dirs[0] 311 newDir = dirs[1] 312 313 err = MigrateWALs(context.Background(), oldDir, newDir, OriginalWAL, c, walpb.Snapshot{}) 314 require.Error(t, err) 315 316 subdirs, err := ioutil.ReadDir(tempDir) 317 require.NoError(t, err) 318 require.Empty(t, subdirs) 319 }