github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/state/raft/storage_test.go (about)

     1  package raft_test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  	"testing"
    11  	"time"
    12  
    13  	"code.cloudfoundry.org/clock/fakeclock"
    14  	"github.com/docker/swarmkit/api"
    15  	"github.com/docker/swarmkit/manager/state/raft"
    16  	"github.com/docker/swarmkit/manager/state/raft/storage"
    17  	raftutils "github.com/docker/swarmkit/manager/state/raft/testutils"
    18  	"github.com/docker/swarmkit/manager/state/store"
    19  	"github.com/docker/swarmkit/testutils"
    20  	"github.com/stretchr/testify/assert"
    21  	"github.com/stretchr/testify/require"
    22  )
    23  
    24  func TestRaftSnapshot(t *testing.T) {
    25  	t.Parallel()
    26  
    27  	// Bring up a 3 node cluster
    28  	nodes, clockSource := raftutils.NewRaftCluster(t, tc, &api.RaftConfig{SnapshotInterval: 9, LogEntriesForSlowFollowers: 0})
    29  	defer raftutils.TeardownCluster(nodes)
    30  
    31  	nodeIDs := []string{"id1", "id2", "id3", "id4", "id5", "id6", "id7", "id8", "id9", "id10", "id11", "id12"}
    32  	values := make([]*api.Node, len(nodeIDs))
    33  	snapshotFilenames := make(map[uint64]string, 4)
    34  
    35  	// Propose 3 values
    36  	var err error
    37  	for i, nodeID := range nodeIDs[:3] {
    38  		values[i], err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeID)
    39  		assert.NoError(t, err, "failed to propose value")
    40  	}
    41  
    42  	// None of the nodes should have snapshot files yet
    43  	for _, node := range nodes {
    44  		dirents, err := ioutil.ReadDir(filepath.Join(node.StateDir, "snap-v3-encrypted"))
    45  		assert.NoError(t, err)
    46  		assert.Len(t, dirents, 0)
    47  	}
    48  
    49  	// Check all nodes have all the data.
    50  	// This also acts as a synchronization point so that the next value we
    51  	// propose will arrive as a separate message to the raft state machine,
    52  	// and it is guaranteed to have the right cluster settings when
    53  	// deciding whether to create a new snapshot.
    54  	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs[:3], values)
    55  
    56  	// Propose a 4th value
    57  	values[3], err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeIDs[3])
    58  	assert.NoError(t, err, "failed to propose value")
    59  
    60  	// All nodes should now have a snapshot file
    61  	for nodeID, node := range nodes {
    62  		assert.NoError(t, testutils.PollFunc(clockSource, func() error {
    63  			dirents, err := ioutil.ReadDir(filepath.Join(node.StateDir, "snap-v3-encrypted"))
    64  			if err != nil {
    65  				return err
    66  			}
    67  			if len(dirents) != 1 {
    68  				return fmt.Errorf("expected 1 snapshot, found %d", len(dirents))
    69  			}
    70  			snapshotFilenames[nodeID] = dirents[0].Name()
    71  			return nil
    72  		}))
    73  	}
    74  
    75  	// Add a node to the cluster
    76  	raftutils.AddRaftNode(t, clockSource, nodes, tc)
    77  
    78  	// It should get a copy of the snapshot
    79  	assert.NoError(t, testutils.PollFunc(clockSource, func() error {
    80  		dirents, err := ioutil.ReadDir(filepath.Join(nodes[4].StateDir, "snap-v3-encrypted"))
    81  		if err != nil {
    82  			return err
    83  		}
    84  		if len(dirents) != 1 {
    85  			return fmt.Errorf("expected 1 snapshot, found %d on new node", len(dirents))
    86  		}
    87  		snapshotFilenames[4] = dirents[0].Name()
    88  		return nil
    89  	}))
    90  
    91  	// It should know about the other nodes
    92  	stripMembers := func(memberList map[uint64]*api.RaftMember) map[uint64]*api.RaftMember {
    93  		raftNodes := make(map[uint64]*api.RaftMember)
    94  		for k, v := range memberList {
    95  			raftNodes[k] = &api.RaftMember{
    96  				RaftID: v.RaftID,
    97  				Addr:   v.Addr,
    98  			}
    99  		}
   100  		return raftNodes
   101  	}
   102  	assert.Equal(t, stripMembers(nodes[1].GetMemberlist()), stripMembers(nodes[4].GetMemberlist()))
   103  
   104  	// All nodes should have all the data
   105  	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs[:4], values)
   106  
   107  	// Propose more values to provoke a second snapshot
   108  	for i := 4; i != len(nodeIDs); i++ {
   109  		values[i], err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeIDs[i])
   110  		assert.NoError(t, err, "failed to propose value")
   111  	}
   112  
   113  	// All nodes should have a snapshot under a *different* name
   114  	for nodeID, node := range nodes {
   115  		assert.NoError(t, testutils.PollFunc(clockSource, func() error {
   116  			dirents, err := ioutil.ReadDir(filepath.Join(node.StateDir, "snap-v3-encrypted"))
   117  			if err != nil {
   118  				return err
   119  			}
   120  			if len(dirents) != 1 {
   121  				return fmt.Errorf("expected 1 snapshot, found %d on node %d", len(dirents), nodeID)
   122  			}
   123  			if dirents[0].Name() == snapshotFilenames[nodeID] {
   124  				return fmt.Errorf("snapshot %s did not get replaced on node %d", snapshotFilenames[nodeID], nodeID)
   125  			}
   126  			return nil
   127  		}))
   128  	}
   129  
   130  	// All nodes should have all the data
   131  	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs, values)
   132  }
   133  
   134  func TestRaftSnapshotRestart(t *testing.T) {
   135  	t.Parallel()
   136  
   137  	// Bring up a 3 node cluster
   138  	nodes, clockSource := raftutils.NewRaftCluster(t, tc, &api.RaftConfig{SnapshotInterval: 10, LogEntriesForSlowFollowers: 0})
   139  	defer raftutils.TeardownCluster(nodes)
   140  
   141  	nodeIDs := []string{"id1", "id2", "id3", "id4", "id5", "id6", "id7"}
   142  	values := make([]*api.Node, len(nodeIDs))
   143  
   144  	// Propose 3 values
   145  	var err error
   146  	for i, nodeID := range nodeIDs[:3] {
   147  		values[i], err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeID)
   148  		assert.NoError(t, err, "failed to propose value")
   149  	}
   150  
   151  	// Take down node 3
   152  	nodes[3].Server.Stop()
   153  	nodes[3].ShutdownRaft()
   154  
   155  	// Propose a 4th value before the snapshot
   156  	values[3], err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeIDs[3])
   157  	assert.NoError(t, err, "failed to propose value")
   158  
   159  	// Remaining nodes shouldn't have snapshot files yet
   160  	for _, node := range []*raftutils.TestNode{nodes[1], nodes[2]} {
   161  		dirents, err := ioutil.ReadDir(filepath.Join(node.StateDir, "snap-v3-encrypted"))
   162  		assert.NoError(t, err)
   163  		assert.Len(t, dirents, 0)
   164  	}
   165  
   166  	// Add a node to the cluster before the snapshot. This is the event
   167  	// that triggers the snapshot.
   168  	nodes[4] = raftutils.NewJoinNode(t, clockSource, nodes[1].Address, tc)
   169  	raftutils.WaitForCluster(t, clockSource, map[uint64]*raftutils.TestNode{1: nodes[1], 2: nodes[2], 4: nodes[4]})
   170  
   171  	// Remaining nodes should now have a snapshot file
   172  	for nodeIdx, node := range []*raftutils.TestNode{nodes[1], nodes[2]} {
   173  		assert.NoError(t, testutils.PollFunc(clockSource, func() error {
   174  			dirents, err := ioutil.ReadDir(filepath.Join(node.StateDir, "snap-v3-encrypted"))
   175  			if err != nil {
   176  				return err
   177  			}
   178  			if len(dirents) != 1 {
   179  				return fmt.Errorf("expected 1 snapshot, found %d on node %d", len(dirents), nodeIdx+1)
   180  			}
   181  			return nil
   182  		}))
   183  	}
   184  	raftutils.CheckValuesOnNodes(t, clockSource, map[uint64]*raftutils.TestNode{1: nodes[1], 2: nodes[2]}, nodeIDs[:4], values[:4])
   185  
   186  	// Propose a 5th value
   187  	values[4], err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeIDs[4])
   188  	require.NoError(t, err)
   189  
   190  	// Add another node to the cluster
   191  	nodes[5] = raftutils.NewJoinNode(t, clockSource, nodes[1].Address, tc)
   192  	raftutils.WaitForCluster(t, clockSource, map[uint64]*raftutils.TestNode{1: nodes[1], 2: nodes[2], 4: nodes[4], 5: nodes[5]})
   193  
   194  	// New node should get a copy of the snapshot
   195  	assert.NoError(t, testutils.PollFunc(clockSource, func() error {
   196  		dirents, err := ioutil.ReadDir(filepath.Join(nodes[5].StateDir, "snap-v3-encrypted"))
   197  		if err != nil {
   198  			return err
   199  		}
   200  		if len(dirents) != 1 {
   201  			return fmt.Errorf("expected 1 snapshot, found %d on new node", len(dirents))
   202  		}
   203  		return nil
   204  	}))
   205  
   206  	dirents, err := ioutil.ReadDir(filepath.Join(nodes[5].StateDir, "snap-v3-encrypted"))
   207  	assert.NoError(t, err)
   208  	assert.Len(t, dirents, 1)
   209  	raftutils.CheckValuesOnNodes(t, clockSource, map[uint64]*raftutils.TestNode{1: nodes[1], 2: nodes[2]}, nodeIDs[:5], values[:5])
   210  
   211  	// It should know about the other nodes, including the one that was just added
   212  	stripMembers := func(memberList map[uint64]*api.RaftMember) map[uint64]*api.RaftMember {
   213  		raftNodes := make(map[uint64]*api.RaftMember)
   214  		for k, v := range memberList {
   215  			raftNodes[k] = &api.RaftMember{
   216  				RaftID: v.RaftID,
   217  				Addr:   v.Addr,
   218  			}
   219  		}
   220  		return raftNodes
   221  	}
   222  	assert.Equal(t, stripMembers(nodes[1].GetMemberlist()), stripMembers(nodes[4].GetMemberlist()))
   223  
   224  	// Restart node 3
   225  	nodes[3] = raftutils.RestartNode(t, clockSource, nodes[3], false)
   226  	raftutils.WaitForCluster(t, clockSource, nodes)
   227  
   228  	// Node 3 should know about other nodes, including the new one
   229  	assert.Len(t, nodes[3].GetMemberlist(), 5)
   230  	assert.Equal(t, stripMembers(nodes[1].GetMemberlist()), stripMembers(nodes[3].GetMemberlist()))
   231  
   232  	// Propose yet another value, to make sure the rejoined node is still
   233  	// receiving new logs
   234  	values[5], err = raftutils.ProposeValue(t, raftutils.Leader(nodes), DefaultProposalTime, nodeIDs[5])
   235  	require.NoError(t, err)
   236  
   237  	// All nodes should have all the data
   238  	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs[:6], values[:6])
   239  
   240  	// Restart node 3 again. It should load the snapshot.
   241  	nodes[3].Server.Stop()
   242  	nodes[3].ShutdownRaft()
   243  	nodes[3] = raftutils.RestartNode(t, clockSource, nodes[3], false)
   244  	raftutils.WaitForCluster(t, clockSource, nodes)
   245  
   246  	assert.Len(t, nodes[3].GetMemberlist(), 5)
   247  	assert.Equal(t, stripMembers(nodes[1].GetMemberlist()), stripMembers(nodes[3].GetMemberlist()))
   248  	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs[:6], values[:6])
   249  
   250  	// Propose again. Just to check consensus after this latest restart.
   251  	values[6], err = raftutils.ProposeValue(t, raftutils.Leader(nodes), DefaultProposalTime, nodeIDs[6])
   252  	require.NoError(t, err)
   253  	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs, values)
   254  }
   255  
   256  func TestRaftSnapshotForceNewCluster(t *testing.T) {
   257  	t.Parallel()
   258  
   259  	// Bring up a 3 node cluster
   260  	nodes, clockSource := raftutils.NewRaftCluster(t, tc, &api.RaftConfig{SnapshotInterval: 10, LogEntriesForSlowFollowers: 0})
   261  	defer raftutils.TeardownCluster(nodes)
   262  
   263  	nodeIDs := []string{"id1", "id2", "id3", "id4", "id5"}
   264  
   265  	// Propose 3 values.
   266  	for _, nodeID := range nodeIDs[:3] {
   267  		_, err := raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeID)
   268  		assert.NoError(t, err, "failed to propose value")
   269  	}
   270  
   271  	// Remove one of the original nodes
   272  
   273  	// Use gRPC instead of calling handler directly because of
   274  	// authorization check.
   275  	cc, err := dial(nodes[1], nodes[1].Address)
   276  	assert.NoError(t, err)
   277  	raftClient := api.NewRaftMembershipClient(cc)
   278  	defer cc.Close()
   279  	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
   280  	resp, err := raftClient.Leave(ctx, &api.LeaveRequest{Node: &api.RaftMember{RaftID: nodes[2].Config.ID}})
   281  	cancel()
   282  	assert.NoError(t, err, "error sending message to leave the raft")
   283  	assert.NotNil(t, resp, "leave response message is nil")
   284  
   285  	raftutils.ShutdownNode(nodes[2])
   286  	delete(nodes, 2)
   287  
   288  	// Nodes shouldn't have snapshot files yet
   289  	for _, node := range nodes {
   290  		dirents, err := ioutil.ReadDir(filepath.Join(node.StateDir, "snap-v3-encrypted"))
   291  		assert.NoError(t, err)
   292  		assert.Len(t, dirents, 0)
   293  	}
   294  
   295  	// Trigger a snapshot, with a 4th proposal
   296  	_, err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeIDs[3])
   297  	assert.NoError(t, err, "failed to propose value")
   298  
   299  	// Nodes should now have a snapshot file
   300  	for nodeIdx, node := range nodes {
   301  		assert.NoError(t, testutils.PollFunc(clockSource, func() error {
   302  			dirents, err := ioutil.ReadDir(filepath.Join(node.StateDir, "snap-v3-encrypted"))
   303  			if err != nil {
   304  				return err
   305  			}
   306  			if len(dirents) != 1 {
   307  				return fmt.Errorf("expected 1 snapshot, found %d on node %d", len(dirents), nodeIdx+1)
   308  			}
   309  			return nil
   310  		}))
   311  	}
   312  
   313  	// Join another node
   314  	nodes[4] = raftutils.NewJoinNode(t, clockSource, nodes[1].Address, tc)
   315  	raftutils.WaitForCluster(t, clockSource, nodes)
   316  
   317  	// Only restart the first node with force-new-cluster option
   318  	nodes[1].Server.Stop()
   319  	nodes[1].ShutdownRaft()
   320  	nodes[1] = raftutils.RestartNode(t, clockSource, nodes[1], true)
   321  	raftutils.WaitForCluster(t, clockSource, map[uint64]*raftutils.TestNode{1: nodes[1]})
   322  
   323  	// The memberlist should contain exactly one node (self)
   324  	memberlist := nodes[1].GetMemberlist()
   325  	require.Len(t, memberlist, 1)
   326  
   327  	// Propose a 5th value
   328  	_, err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeIDs[4])
   329  	require.NoError(t, err)
   330  }
   331  
   332  func TestGCWAL(t *testing.T) {
   333  	t.Parallel()
   334  
   335  	// Additional log entries from cluster setup, leader election
   336  	extraLogEntries := 5
   337  	// Number of large entries to propose
   338  	proposals := 8
   339  
   340  	// Bring up a 3 node cluster
   341  	nodes, clockSource := raftutils.NewRaftCluster(t, tc, &api.RaftConfig{SnapshotInterval: uint64(proposals + extraLogEntries), LogEntriesForSlowFollowers: 0})
   342  
   343  	for i := 0; i != proposals; i++ {
   344  		_, err := proposeLargeValue(t, nodes[1], DefaultProposalTime, fmt.Sprintf("id%d", i))
   345  		assert.NoError(t, err, "failed to propose value")
   346  	}
   347  
   348  	time.Sleep(250 * time.Millisecond)
   349  
   350  	// Snapshot should have been triggered just as the WAL rotated, so
   351  	// both WAL files should be preserved
   352  	assert.NoError(t, testutils.PollFunc(clockSource, func() error {
   353  		dirents, err := ioutil.ReadDir(filepath.Join(nodes[1].StateDir, "snap-v3-encrypted"))
   354  		if err != nil {
   355  			return err
   356  		}
   357  		if len(dirents) != 1 {
   358  			return fmt.Errorf("expected 1 snapshot, found %d", len(dirents))
   359  		}
   360  
   361  		dirents, err = ioutil.ReadDir(filepath.Join(nodes[1].StateDir, "wal-v3-encrypted"))
   362  		if err != nil {
   363  			return err
   364  		}
   365  		var walCount int
   366  		for _, f := range dirents {
   367  			if strings.HasSuffix(f.Name(), ".wal") {
   368  				walCount++
   369  			}
   370  		}
   371  		if walCount != 2 {
   372  			return fmt.Errorf("expected 2 WAL files, found %d", walCount)
   373  		}
   374  		return nil
   375  	}))
   376  
   377  	raftutils.TeardownCluster(nodes)
   378  
   379  	// Repeat this test, but trigger the snapshot after the WAL has rotated
   380  	proposals++
   381  	nodes, clockSource = raftutils.NewRaftCluster(t, tc, &api.RaftConfig{SnapshotInterval: uint64(proposals + extraLogEntries), LogEntriesForSlowFollowers: 0})
   382  	defer raftutils.TeardownCluster(nodes)
   383  
   384  	for i := 0; i != proposals; i++ {
   385  		_, err := proposeLargeValue(t, nodes[1], DefaultProposalTime, fmt.Sprintf("id%d", i))
   386  		assert.NoError(t, err, "failed to propose value")
   387  	}
   388  
   389  	time.Sleep(250 * time.Millisecond)
   390  
   391  	// This time only one WAL file should be saved.
   392  	assert.NoError(t, testutils.PollFunc(clockSource, func() error {
   393  		dirents, err := ioutil.ReadDir(filepath.Join(nodes[1].StateDir, "snap-v3-encrypted"))
   394  		if err != nil {
   395  			return err
   396  		}
   397  
   398  		if len(dirents) != 1 {
   399  			return fmt.Errorf("expected 1 snapshot, found %d", len(dirents))
   400  		}
   401  
   402  		dirents, err = ioutil.ReadDir(filepath.Join(nodes[1].StateDir, "wal-v3-encrypted"))
   403  		if err != nil {
   404  			return err
   405  		}
   406  		var walCount int
   407  		for _, f := range dirents {
   408  			if strings.HasSuffix(f.Name(), ".wal") {
   409  				walCount++
   410  			}
   411  		}
   412  		if walCount != 1 {
   413  			return fmt.Errorf("expected 1 WAL file, found %d", walCount)
   414  		}
   415  		return nil
   416  	}))
   417  
   418  	// Restart the whole cluster
   419  	for _, node := range nodes {
   420  		node.Server.Stop()
   421  		node.ShutdownRaft()
   422  	}
   423  
   424  	raftutils.AdvanceTicks(clockSource, 5)
   425  
   426  	i := 0
   427  	for k, node := range nodes {
   428  		nodes[k] = raftutils.RestartNode(t, clockSource, node, false)
   429  		i++
   430  	}
   431  	raftutils.WaitForCluster(t, clockSource, nodes)
   432  
   433  	// Is the data intact after restart?
   434  	for _, node := range nodes {
   435  		assert.NoError(t, testutils.PollFunc(clockSource, func() error {
   436  			var err error
   437  			node.MemoryStore().View(func(tx store.ReadTx) {
   438  				var allNodes []*api.Node
   439  				allNodes, err = store.FindNodes(tx, store.All)
   440  				if err != nil {
   441  					return
   442  				}
   443  				if len(allNodes) != proposals {
   444  					err = fmt.Errorf("expected %d nodes, got %d", proposals, len(allNodes))
   445  					return
   446  				}
   447  			})
   448  			return err
   449  		}))
   450  	}
   451  
   452  	// It should still be possible to propose values
   453  	_, err := raftutils.ProposeValue(t, raftutils.Leader(nodes), DefaultProposalTime, "newnode")
   454  	assert.NoError(t, err, "failed to propose value")
   455  
   456  	for _, node := range nodes {
   457  		assert.NoError(t, testutils.PollFunc(clockSource, func() error {
   458  			var err error
   459  			node.MemoryStore().View(func(tx store.ReadTx) {
   460  				var allNodes []*api.Node
   461  				allNodes, err = store.FindNodes(tx, store.All)
   462  				if err != nil {
   463  					return
   464  				}
   465  				if len(allNodes) != proposals+1 {
   466  					err = fmt.Errorf("expected %d nodes, got %d", proposals, len(allNodes))
   467  					return
   468  				}
   469  			})
   470  			return err
   471  		}))
   472  	}
   473  }
   474  
   475  // proposeLargeValue proposes a 10kb value to a raft test cluster
   476  func proposeLargeValue(t *testing.T, raftNode *raftutils.TestNode, time time.Duration, nodeID ...string) (*api.Node, error) {
   477  	nodeIDStr := "id1"
   478  	if len(nodeID) != 0 {
   479  		nodeIDStr = nodeID[0]
   480  	}
   481  	a := make([]byte, 10000)
   482  	for i := 0; i != len(a); i++ {
   483  		a[i] = 'a'
   484  	}
   485  	node := &api.Node{
   486  		ID: nodeIDStr,
   487  		Spec: api.NodeSpec{
   488  			Annotations: api.Annotations{
   489  				Name: nodeIDStr,
   490  				Labels: map[string]string{
   491  					"largestring": string(a),
   492  				},
   493  			},
   494  		},
   495  	}
   496  
   497  	storeActions := []api.StoreAction{
   498  		{
   499  			Action: api.StoreActionKindCreate,
   500  			Target: &api.StoreAction_Node{
   501  				Node: node,
   502  			},
   503  		},
   504  	}
   505  
   506  	ctx, cancel := context.WithTimeout(context.Background(), time)
   507  
   508  	err := raftNode.ProposeValue(ctx, storeActions, func() {
   509  		err := raftNode.MemoryStore().ApplyStoreActions(storeActions)
   510  		assert.NoError(t, err, "error applying actions")
   511  	})
   512  	cancel()
   513  	if err != nil {
   514  		return nil, err
   515  	}
   516  
   517  	return node, nil
   518  }
   519  
   520  // This test rotates the encryption key and waits for the expected thing to happen
   521  func TestRaftEncryptionKeyRotationWait(t *testing.T) {
   522  	t.Parallel()
   523  	nodes := make(map[uint64]*raftutils.TestNode)
   524  	var clockSource *fakeclock.FakeClock
   525  
   526  	raftConfig := raft.DefaultRaftConfig()
   527  	nodes[1], clockSource = raftutils.NewInitNode(t, tc, &raftConfig)
   528  	defer raftutils.TeardownCluster(nodes)
   529  
   530  	nodeIDs := []string{"id1", "id2", "id3"}
   531  	values := make([]*api.Node, len(nodeIDs))
   532  
   533  	// Propose 3 values
   534  	var err error
   535  	for i, nodeID := range nodeIDs[:3] {
   536  		values[i], err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, nodeID)
   537  		require.NoError(t, err, "failed to propose value")
   538  	}
   539  
   540  	snapDir := filepath.Join(nodes[1].StateDir, "snap-v3-encrypted")
   541  
   542  	startingKeys := nodes[1].KeyRotator.GetKeys()
   543  
   544  	// rotate the encryption key
   545  	nodes[1].KeyRotator.QueuePendingKey([]byte("key2"))
   546  	nodes[1].KeyRotator.RotationNotify() <- struct{}{}
   547  
   548  	// the rotation should trigger a snapshot, which should notify the rotator when it's done
   549  	require.NoError(t, testutils.PollFunc(clockSource, func() error {
   550  		snapshots, err := storage.ListSnapshots(snapDir)
   551  		if err != nil {
   552  			return err
   553  		}
   554  		if len(snapshots) != 1 {
   555  			return fmt.Errorf("expected 1 snapshot, found %d on new node", len(snapshots))
   556  		}
   557  		if nodes[1].KeyRotator.NeedsRotation() {
   558  			return fmt.Errorf("rotation never finished")
   559  		}
   560  		return nil
   561  	}))
   562  	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs, values)
   563  
   564  	// Propose a 4th value
   565  	nodeIDs = append(nodeIDs, "id4")
   566  	v, err := raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, "id4")
   567  	require.NoError(t, err, "failed to propose value")
   568  	values = append(values, v)
   569  	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs, values)
   570  
   571  	nodes[1].Server.Stop()
   572  	nodes[1].ShutdownRaft()
   573  
   574  	// Try to restart node 1. Without the new unlock key, it can't actually start
   575  	n, ctx := raftutils.CopyNode(t, clockSource, nodes[1], false, raftutils.NewSimpleKeyRotator(startingKeys))
   576  	require.Error(t, n.Node.JoinAndStart(ctx),
   577  		"should not have been able to restart since we can't read snapshots")
   578  
   579  	// with the right key, it can start, even if the right key is only the pending key
   580  	newKeys := startingKeys
   581  	newKeys.PendingDEK = []byte("key2")
   582  	nodes[1].KeyRotator = raftutils.NewSimpleKeyRotator(newKeys)
   583  	nodes[1] = raftutils.RestartNode(t, clockSource, nodes[1], false)
   584  
   585  	raftutils.WaitForCluster(t, clockSource, nodes)
   586  
   587  	// as soon as we joined, it should have finished rotating the key
   588  	require.False(t, nodes[1].KeyRotator.NeedsRotation())
   589  	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs, values)
   590  
   591  	// break snapshotting, and ensure that key rotation never finishes
   592  	tempSnapDir := filepath.Join(nodes[1].StateDir, "snap-backup")
   593  	require.NoError(t, os.Rename(snapDir, tempSnapDir))
   594  	require.NoError(t, ioutil.WriteFile(snapDir, []byte("this is no longer a directory"), 0644))
   595  
   596  	nodes[1].KeyRotator.QueuePendingKey([]byte("key3"))
   597  	nodes[1].KeyRotator.RotationNotify() <- struct{}{}
   598  
   599  	time.Sleep(250 * time.Millisecond)
   600  
   601  	// rotation has not been finished, because we cannot take a snapshot
   602  	require.True(t, nodes[1].KeyRotator.NeedsRotation())
   603  
   604  	// Propose a 5th value, so we have WALs written with the new key
   605  	nodeIDs = append(nodeIDs, "id5")
   606  	v, err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, "id5")
   607  	require.NoError(t, err, "failed to propose value")
   608  	values = append(values, v)
   609  	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs, values)
   610  
   611  	nodes[1].Server.Stop()
   612  	nodes[1].ShutdownRaft()
   613  
   614  	// restore the snapshot dir
   615  	require.NoError(t, os.RemoveAll(snapDir))
   616  	require.NoError(t, os.Rename(tempSnapDir, snapDir))
   617  
   618  	// Now the wals are a mix of key2 and key3 - we can't actually start with either key
   619  	singleKey := raft.EncryptionKeys{CurrentDEK: []byte("key2")}
   620  	n, ctx = raftutils.CopyNode(t, clockSource, nodes[1], false, raftutils.NewSimpleKeyRotator(singleKey))
   621  	require.Error(t, n.Node.JoinAndStart(ctx),
   622  		"should not have been able to restart since we can't read all the WALs, even if we can read the snapshot")
   623  	singleKey = raft.EncryptionKeys{CurrentDEK: []byte("key3")}
   624  	n, ctx = raftutils.CopyNode(t, clockSource, nodes[1], false, raftutils.NewSimpleKeyRotator(singleKey))
   625  	require.Error(t, n.Node.JoinAndStart(ctx),
   626  		"should not have been able to restart since we can't read all the WALs, and also not the snapshot")
   627  
   628  	nodes[1], ctx = raftutils.CopyNode(t, clockSource, nodes[1], false,
   629  		raftutils.NewSimpleKeyRotator(raft.EncryptionKeys{
   630  			CurrentDEK: []byte("key2"),
   631  			PendingDEK: []byte("key3"),
   632  		}))
   633  	require.NoError(t, nodes[1].Node.JoinAndStart(ctx))
   634  
   635  	// we can load, but we still need a snapshot because rotation hasn't finished
   636  	snapshots, err := storage.ListSnapshots(snapDir)
   637  	require.NoError(t, err)
   638  	require.Len(t, snapshots, 1, "expected 1 snapshot")
   639  	require.True(t, nodes[1].KeyRotator.NeedsRotation())
   640  	currSnapshot := snapshots[0]
   641  
   642  	// start the node - everything should fix itself
   643  	go nodes[1].Node.Run(ctx)
   644  	raftutils.WaitForCluster(t, clockSource, nodes)
   645  
   646  	require.NoError(t, testutils.PollFunc(clockSource, func() error {
   647  		snapshots, err := storage.ListSnapshots(snapDir)
   648  		if err != nil {
   649  			return err
   650  		}
   651  		if len(snapshots) != 1 {
   652  			return fmt.Errorf("expected 1 snapshots, found %d on new node", len(snapshots))
   653  		}
   654  		if snapshots[0] == currSnapshot {
   655  			return fmt.Errorf("new snapshot not done yet")
   656  		}
   657  		if nodes[1].KeyRotator.NeedsRotation() {
   658  			return fmt.Errorf("rotation never finished")
   659  		}
   660  		currSnapshot = snapshots[0]
   661  		return nil
   662  	}))
   663  	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs, values)
   664  
   665  	// If we can't update the keys, we wait for the next snapshot to do so
   666  	nodes[1].KeyRotator.SetUpdateFunc(func() error { return fmt.Errorf("nope!") })
   667  	nodes[1].KeyRotator.QueuePendingKey([]byte("key4"))
   668  	nodes[1].KeyRotator.RotationNotify() <- struct{}{}
   669  
   670  	require.NoError(t, testutils.PollFunc(clockSource, func() error {
   671  		snapshots, err := storage.ListSnapshots(snapDir)
   672  		if err != nil {
   673  			return err
   674  		}
   675  		if len(snapshots) != 1 {
   676  			return fmt.Errorf("expected 1 snapshots, found %d on new node", len(snapshots))
   677  		}
   678  		if snapshots[0] == currSnapshot {
   679  			return fmt.Errorf("new snapshot not done yet")
   680  		}
   681  		currSnapshot = snapshots[0]
   682  		return nil
   683  	}))
   684  	require.True(t, nodes[1].KeyRotator.NeedsRotation())
   685  
   686  	// Fix updating the key rotator, and propose a 6th value - this should trigger the key
   687  	// rotation to finish
   688  	nodes[1].KeyRotator.SetUpdateFunc(nil)
   689  	nodeIDs = append(nodeIDs, "id6")
   690  	v, err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, "id6")
   691  	require.NoError(t, err, "failed to propose value")
   692  	values = append(values, v)
   693  	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs, values)
   694  
   695  	require.NoError(t, testutils.PollFunc(clockSource, func() error {
   696  		if nodes[1].KeyRotator.NeedsRotation() {
   697  			return fmt.Errorf("rotation never finished")
   698  		}
   699  		return nil
   700  	}))
   701  
   702  	// no new snapshot
   703  	snapshots, err = storage.ListSnapshots(snapDir)
   704  	require.NoError(t, err)
   705  	require.Len(t, snapshots, 1)
   706  	require.Equal(t, currSnapshot, snapshots[0])
   707  
   708  	// Even if something goes wrong with getting keys, and needs rotation returns a false positive,
   709  	// if there's no PendingDEK nothing happens.
   710  
   711  	fakeTrue := true
   712  	nodes[1].KeyRotator.SetNeedsRotation(&fakeTrue)
   713  	nodes[1].KeyRotator.RotationNotify() <- struct{}{}
   714  
   715  	// propose another value
   716  	nodeIDs = append(nodeIDs, "id7")
   717  	v, err = raftutils.ProposeValue(t, nodes[1], DefaultProposalTime, "id7")
   718  	require.NoError(t, err, "failed to propose value")
   719  	values = append(values, v)
   720  	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs, values)
   721  
   722  	// no new snapshot
   723  	snapshots, err = storage.ListSnapshots(snapDir)
   724  	require.NoError(t, err)
   725  	require.Len(t, snapshots, 1)
   726  	require.Equal(t, currSnapshot, snapshots[0])
   727  
   728  	// and when we restart, we can restart with the original key (the WAL written for the new proposed value)
   729  	// is written with the old key
   730  	nodes[1].Server.Stop()
   731  	nodes[1].ShutdownRaft()
   732  
   733  	nodes[1].KeyRotator = raftutils.NewSimpleKeyRotator(raft.EncryptionKeys{
   734  		CurrentDEK: []byte("key4"),
   735  	})
   736  	nodes[1] = raftutils.RestartNode(t, clockSource, nodes[1], false)
   737  	raftutils.WaitForCluster(t, clockSource, nodes)
   738  	raftutils.CheckValuesOnNodes(t, clockSource, nodes, nodeIDs, values)
   739  }
   740  
   741  // This test rotates the encryption key and restarts the node - the intent is try to trigger
   742  // race conditions if there is more than one node and hence consensus may take longer.
   743  func TestRaftEncryptionKeyRotationStress(t *testing.T) {
   744  	t.Parallel()
   745  
   746  	// Bring up a 3 nodes cluster
   747  	nodes, clockSource := raftutils.NewRaftCluster(t, tc)
   748  	defer raftutils.TeardownCluster(nodes)
   749  	leader := nodes[1]
   750  
   751  	// constantly propose values
   752  	done, stop, restart, clusterReady := make(chan struct{}), make(chan struct{}), make(chan struct{}), make(chan struct{})
   753  	go func() {
   754  		counter := len(nodes)
   755  		for {
   756  			select {
   757  			case <-stop:
   758  				close(done)
   759  				return
   760  			case <-restart:
   761  				// the node restarts may trigger a leadership change, so wait until the cluster has 3
   762  				// nodes again and a leader is selected before proposing more values
   763  				<-clusterReady
   764  				leader = raftutils.Leader(nodes)
   765  			default:
   766  				counter += 1
   767  				raftutils.ProposeValue(t, leader, DefaultProposalTime, fmt.Sprintf("id%d", counter))
   768  			}
   769  		}
   770  	}()
   771  
   772  	for i := 0; i < 30; i++ {
   773  		// rotate the encryption key
   774  		nodes[3].KeyRotator.QueuePendingKey([]byte(fmt.Sprintf("newKey%d", i)))
   775  		nodes[3].KeyRotator.RotationNotify() <- struct{}{}
   776  
   777  		require.NoError(t, testutils.PollFunc(clockSource, func() error {
   778  			if nodes[3].KeyRotator.GetKeys().PendingDEK == nil {
   779  				return nil
   780  			}
   781  			return fmt.Errorf("not done rotating yet")
   782  		}))
   783  
   784  		// restart the node and wait for everything to settle and a leader to be elected
   785  		nodes[3].Server.Stop()
   786  		nodes[3].ShutdownRaft()
   787  		restart <- struct{}{}
   788  		nodes[3] = raftutils.RestartNode(t, clockSource, nodes[3], false)
   789  		raftutils.AdvanceTicks(clockSource, 1)
   790  
   791  		raftutils.WaitForCluster(t, clockSource, nodes)
   792  		clusterReady <- struct{}{}
   793  	}
   794  
   795  	close(stop)
   796  	<-done
   797  }