...

Source file src/go.etcd.io/etcd/raft/v3/rafttest/interaction_env_handler_add_nodes.go

Documentation: go.etcd.io/etcd/raft/v3/rafttest

     1  // Copyright 2019 The etcd Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package rafttest
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"reflect"
    21  	"testing"
    22  
    23  	"github.com/cockroachdb/datadriven"
    24  	"go.etcd.io/etcd/raft/v3"
    25  	pb "go.etcd.io/etcd/raft/v3/raftpb"
    26  )
    27  
    28  func (env *InteractionEnv) handleAddNodes(t *testing.T, d datadriven.TestData) error {
    29  	n := firstAsInt(t, d)
    30  	var snap pb.Snapshot
    31  	for _, arg := range d.CmdArgs[1:] {
    32  		for i := range arg.Vals {
    33  			switch arg.Key {
    34  			case "voters":
    35  				var id uint64
    36  				arg.Scan(t, i, &id)
    37  				snap.Metadata.ConfState.Voters = append(snap.Metadata.ConfState.Voters, id)
    38  			case "learners":
    39  				var id uint64
    40  				arg.Scan(t, i, &id)
    41  				snap.Metadata.ConfState.Learners = append(snap.Metadata.ConfState.Learners, id)
    42  			case "index":
    43  				arg.Scan(t, i, &snap.Metadata.Index)
    44  			case "content":
    45  				arg.Scan(t, i, &snap.Data)
    46  			}
    47  		}
    48  	}
    49  	return env.AddNodes(n, snap)
    50  }
    51  
    52  type snapOverrideStorage struct {
    53  	Storage
    54  	snapshotOverride func() (pb.Snapshot, error)
    55  }
    56  
    57  func (s snapOverrideStorage) Snapshot() (pb.Snapshot, error) {
    58  	if s.snapshotOverride != nil {
    59  		return s.snapshotOverride()
    60  	}
    61  	return s.Storage.Snapshot()
    62  }
    63  
    64  var _ raft.Storage = snapOverrideStorage{}
    65  
    66  // AddNodes adds n new nodes initializes from the given snapshot (which may be
    67  // empty). They will be assigned consecutive IDs.
    68  func (env *InteractionEnv) AddNodes(n int, snap pb.Snapshot) error {
    69  	bootstrap := !reflect.DeepEqual(snap, pb.Snapshot{})
    70  	for i := 0; i < n; i++ {
    71  		id := uint64(1 + len(env.Nodes))
    72  		s := snapOverrideStorage{
    73  			Storage: raft.NewMemoryStorage(),
    74  			// When you ask for a snapshot, you get the most recent snapshot.
    75  			//
    76  			// TODO(tbg): this is sort of clunky, but MemoryStorage itself will
    77  			// give you some fixed snapshot and also the snapshot changes
    78  			// whenever you compact the logs and vice versa, so it's all a bit
    79  			// awkward to use.
    80  			snapshotOverride: func() (pb.Snapshot, error) {
    81  				snaps := env.Nodes[int(id-1)].History
    82  				return snaps[len(snaps)-1], nil
    83  			},
    84  		}
    85  		if bootstrap {
    86  			// NB: we could make this work with 1, but MemoryStorage just
    87  			// doesn't play well with that and it's not a loss of generality.
    88  			if snap.Metadata.Index <= 1 {
    89  				return errors.New("index must be specified as > 1 due to bootstrap")
    90  			}
    91  			snap.Metadata.Term = 1
    92  			if err := s.ApplySnapshot(snap); err != nil {
    93  				return err
    94  			}
    95  			fi, err := s.FirstIndex()
    96  			if err != nil {
    97  				return err
    98  			}
    99  			// At the time of writing and for *MemoryStorage, applying a
   100  			// snapshot also truncates appropriately, but this would change with
   101  			// other storage engines potentially.
   102  			if exp := snap.Metadata.Index + 1; fi != exp {
   103  				return fmt.Errorf("failed to establish first index %d; got %d", exp, fi)
   104  			}
   105  		}
   106  		cfg := defaultRaftConfig(id, snap.Metadata.Index, s)
   107  		if env.Options.OnConfig != nil {
   108  			env.Options.OnConfig(cfg)
   109  			if cfg.ID != id {
   110  				// This could be supported but then we need to do more work
   111  				// translating back and forth -- not worth it.
   112  				return errors.New("OnConfig must not change the ID")
   113  			}
   114  		}
   115  		if cfg.Logger != nil {
   116  			return errors.New("OnConfig must not set Logger")
   117  		}
   118  		cfg.Logger = env.Output
   119  
   120  		rn, err := raft.NewRawNode(cfg)
   121  		if err != nil {
   122  			return err
   123  		}
   124  
   125  		node := Node{
   126  			RawNode: rn,
   127  			// TODO(tbg): allow a more general Storage, as long as it also allows
   128  			// us to apply snapshots, append entries, and update the HardState.
   129  			Storage: s,
   130  			Config:  cfg,
   131  			History: []pb.Snapshot{snap},
   132  		}
   133  		env.Nodes = append(env.Nodes, node)
   134  	}
   135  	return nil
   136  }
   137  

View as plain text