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  }