github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/state/raft/storage/storage_test.go (about) 1 package storage 2 3 import ( 4 "context" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "testing" 9 10 "github.com/coreos/etcd/raft/raftpb" 11 "github.com/coreos/etcd/wal/walpb" 12 "github.com/docker/swarmkit/manager/encryption" 13 "github.com/pkg/errors" 14 "github.com/stretchr/testify/require" 15 ) 16 17 func TestBootstrapFromDisk(t *testing.T) { 18 tempdir, err := ioutil.TempDir("", "raft-storage") 19 require.NoError(t, err) 20 defer os.RemoveAll(tempdir) 21 22 logger := EncryptedRaftLogger{ 23 StateDir: tempdir, 24 EncryptionKey: []byte("key1"), 25 } 26 err = logger.BootstrapNew([]byte("metadata")) 27 require.NoError(t, err) 28 29 // everything should be saved with "key1" 30 _, entries, _ := makeWALData(0, 0) 31 err = logger.SaveEntries(raftpb.HardState{}, entries) 32 require.NoError(t, err) 33 logger.Close(context.Background()) 34 35 // now we can bootstrap from disk, even if there is no snapshot 36 logger = EncryptedRaftLogger{ 37 StateDir: tempdir, 38 EncryptionKey: []byte("key1"), 39 } 40 readSnap, waldata, err := logger.BootstrapFromDisk(context.Background()) 41 require.NoError(t, err) 42 require.Nil(t, readSnap) 43 require.Equal(t, entries, waldata.Entries) 44 45 // save a snapshot 46 snapshot := fakeSnapshotData 47 err = logger.SaveSnapshot(snapshot) 48 require.NoError(t, err) 49 _, entries, _ = makeWALData(snapshot.Metadata.Index, snapshot.Metadata.Term) 50 err = logger.SaveEntries(raftpb.HardState{}, entries) 51 require.NoError(t, err) 52 logger.Close(context.Background()) 53 54 // load snapshots and wals 55 logger = EncryptedRaftLogger{ 56 StateDir: tempdir, 57 EncryptionKey: []byte("key1"), 58 } 59 readSnap, waldata, err = logger.BootstrapFromDisk(context.Background()) 60 require.NoError(t, err) 61 require.NotNil(t, snapshot) 62 require.Equal(t, snapshot, *readSnap) 63 require.Equal(t, entries, waldata.Entries) 64 65 // start writing more wals and rotate in the middle 66 _, entries, _ = makeWALData(snapshot.Metadata.Index, snapshot.Metadata.Term) 67 err = logger.SaveEntries(raftpb.HardState{}, entries[:1]) 68 require.NoError(t, err) 69 logger.RotateEncryptionKey([]byte("key2")) 70 err = logger.SaveEntries(raftpb.HardState{}, entries[1:]) 71 require.NoError(t, err) 72 logger.Close(context.Background()) 73 74 // we can't bootstrap from disk using only the first or second key 75 for _, key := range []string{"key1", "key2"} { 76 logger := EncryptedRaftLogger{ 77 StateDir: tempdir, 78 EncryptionKey: []byte(key), 79 } 80 _, _, err := logger.BootstrapFromDisk(context.Background()) 81 require.IsType(t, encryption.ErrCannotDecrypt{}, errors.Cause(err)) 82 } 83 84 // but we can if we combine the two keys, we can bootstrap just fine 85 logger = EncryptedRaftLogger{ 86 StateDir: tempdir, 87 EncryptionKey: []byte("key2"), 88 } 89 readSnap, waldata, err = logger.BootstrapFromDisk(context.Background(), []byte("key1")) 90 require.NoError(t, err) 91 require.NotNil(t, snapshot) 92 require.Equal(t, snapshot, *readSnap) 93 require.Equal(t, entries, waldata.Entries) 94 } 95 96 // Ensure that we can change encoding and not have a race condition 97 func TestRaftLoggerRace(t *testing.T) { 98 tempdir, err := ioutil.TempDir("", "raft-storage") 99 require.NoError(t, err) 100 defer os.RemoveAll(tempdir) 101 102 logger := EncryptedRaftLogger{ 103 StateDir: tempdir, 104 EncryptionKey: []byte("Hello"), 105 } 106 err = logger.BootstrapNew([]byte("metadata")) 107 require.NoError(t, err) 108 109 _, entries, _ := makeWALData(fakeSnapshotData.Metadata.Index, fakeSnapshotData.Metadata.Term) 110 111 done1 := make(chan error) 112 done2 := make(chan error) 113 done3 := make(chan error) 114 done4 := make(chan error) 115 go func() { 116 done1 <- logger.SaveSnapshot(fakeSnapshotData) 117 }() 118 go func() { 119 done2 <- logger.SaveEntries(raftpb.HardState{}, entries) 120 }() 121 go func() { 122 logger.RotateEncryptionKey([]byte("Hello 2")) 123 done3 <- nil 124 }() 125 go func() { 126 done4 <- logger.SaveSnapshot(fakeSnapshotData) 127 }() 128 129 err = <-done1 130 require.NoError(t, err, "unable to save snapshot") 131 132 err = <-done2 133 require.NoError(t, err, "unable to save entries") 134 135 err = <-done3 136 require.NoError(t, err, "unable to rotate key") 137 138 err = <-done4 139 require.NoError(t, err, "unable to save snapshot a second time") 140 } 141 142 func TestMigrateToV3EncryptedForm(t *testing.T) { 143 t.Parallel() 144 145 tempdir, err := ioutil.TempDir("", "raft-storage") 146 require.NoError(t, err) 147 defer os.RemoveAll(tempdir) 148 149 dek := []byte("key") 150 151 writeDataTo := func(suffix string, snapshot raftpb.Snapshot, walFactory WALFactory, snapFactory SnapFactory) []raftpb.Entry { 152 snapDir := filepath.Join(tempdir, "snap"+suffix) 153 walDir := filepath.Join(tempdir, "wal"+suffix) 154 require.NoError(t, os.MkdirAll(snapDir, 0755)) 155 require.NoError(t, snapFactory.New(snapDir).SaveSnap(snapshot)) 156 157 _, entries, _ := makeWALData(snapshot.Metadata.Index, snapshot.Metadata.Term) 158 walWriter, err := walFactory.Create(walDir, []byte("metadata")) 159 require.NoError(t, err) 160 require.NoError(t, walWriter.SaveSnapshot(walpb.Snapshot{Index: snapshot.Metadata.Index, Term: snapshot.Metadata.Term})) 161 require.NoError(t, walWriter.Save(raftpb.HardState{}, entries)) 162 require.NoError(t, walWriter.Close()) 163 return entries 164 } 165 166 requireLoadedData := func(expectedSnap raftpb.Snapshot, expectedEntries []raftpb.Entry) { 167 logger := EncryptedRaftLogger{ 168 StateDir: tempdir, 169 EncryptionKey: dek, 170 } 171 readSnap, waldata, err := logger.BootstrapFromDisk(context.Background()) 172 require.NoError(t, err) 173 require.NotNil(t, readSnap) 174 require.Equal(t, expectedSnap, *readSnap) 175 require.Equal(t, expectedEntries, waldata.Entries) 176 logger.Close(context.Background()) 177 } 178 179 v2Snapshot := fakeSnapshotData 180 v3Snapshot := fakeSnapshotData 181 v3Snapshot.Metadata.Index += 100 182 v3Snapshot.Metadata.Term += 10 183 v3EncryptedSnapshot := fakeSnapshotData 184 v3EncryptedSnapshot.Metadata.Index += 200 185 v3EncryptedSnapshot.Metadata.Term += 20 186 187 encoder, decoders := encryption.Defaults(dek, false) 188 walFactory := NewWALFactory(encoder, decoders) 189 snapFactory := NewSnapFactory(encoder, decoders) 190 191 // generate both v2 and v3 unencrypted snapshot data directories, as well as an encrypted directory 192 v2Entries := writeDataTo("", v2Snapshot, OriginalWAL, OriginalSnap) 193 v3Entries := writeDataTo("-v3", v3Snapshot, OriginalWAL, OriginalSnap) 194 v3EncryptedEntries := writeDataTo("-v3-encrypted", v3EncryptedSnapshot, walFactory, snapFactory) 195 196 // bootstrap from disk - the encrypted directory exists, so we should just read from 197 // it instead of from the legacy directories 198 requireLoadedData(v3EncryptedSnapshot, v3EncryptedEntries) 199 200 // remove the newest dirs - should try to migrate from v3 201 require.NoError(t, os.RemoveAll(filepath.Join(tempdir, "snap-v3-encrypted"))) 202 require.NoError(t, os.RemoveAll(filepath.Join(tempdir, "wal-v3-encrypted"))) 203 requireLoadedData(v3Snapshot, v3Entries) 204 // it can recover from partial migrations 205 require.NoError(t, os.RemoveAll(filepath.Join(tempdir, "snap-v3-encrypted"))) 206 requireLoadedData(v3Snapshot, v3Entries) 207 // v3 dirs still there 208 _, err = os.Stat(filepath.Join(tempdir, "snap-v3")) 209 require.NoError(t, err) 210 _, err = os.Stat(filepath.Join(tempdir, "wal-v3")) 211 require.NoError(t, err) 212 213 // remove the v3 dirs - should try to migrate from v2 214 require.NoError(t, os.RemoveAll(filepath.Join(tempdir, "snap-v3-encrypted"))) 215 require.NoError(t, os.RemoveAll(filepath.Join(tempdir, "wal-v3-encrypted"))) 216 require.NoError(t, os.RemoveAll(filepath.Join(tempdir, "snap-v3"))) 217 require.NoError(t, os.RemoveAll(filepath.Join(tempdir, "wal-v3"))) 218 requireLoadedData(v2Snapshot, v2Entries) 219 }