1 // Copyright 2015 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 raft 16 17 import ( 18 "errors" 19 20 pb "go.etcd.io/etcd/raft/v3/raftpb" 21 ) 22 23 // Bootstrap initializes the RawNode for first use by appending configuration 24 // changes for the supplied peers. This method returns an error if the Storage 25 // is nonempty. 26 // 27 // It is recommended that instead of calling this method, applications bootstrap 28 // their state manually by setting up a Storage that has a first index > 1 and 29 // which stores the desired ConfState as its InitialState. 30 func (rn *RawNode) Bootstrap(peers []Peer) error { 31 if len(peers) == 0 { 32 return errors.New("must provide at least one peer to Bootstrap") 33 } 34 lastIndex, err := rn.raft.raftLog.storage.LastIndex() 35 if err != nil { 36 return err 37 } 38 39 if lastIndex != 0 { 40 return errors.New("can't bootstrap a nonempty Storage") 41 } 42 43 // We've faked out initial entries above, but nothing has been 44 // persisted. Start with an empty HardState (thus the first Ready will 45 // emit a HardState update for the app to persist). 46 rn.prevHardSt = emptyState 47 48 // TODO(tbg): remove StartNode and give the application the right tools to 49 // bootstrap the initial membership in a cleaner way. 50 rn.raft.becomeFollower(1, None) 51 ents := make([]pb.Entry, len(peers)) 52 for i, peer := range peers { 53 cc := pb.ConfChange{Type: pb.ConfChangeAddNode, NodeID: peer.ID, Context: peer.Context} 54 data, err := cc.Marshal() 55 if err != nil { 56 return err 57 } 58 59 ents[i] = pb.Entry{Type: pb.EntryConfChange, Term: 1, Index: uint64(i + 1), Data: data} 60 } 61 rn.raft.raftLog.append(ents...) 62 63 // Now apply them, mainly so that the application can call Campaign 64 // immediately after StartNode in tests. Note that these nodes will 65 // be added to raft twice: here and when the application's Ready 66 // loop calls ApplyConfChange. The calls to addNode must come after 67 // all calls to raftLog.append so progress.next is set after these 68 // bootstrapping entries (it is an error if we try to append these 69 // entries since they have already been committed). 70 // We do not set raftLog.applied so the application will be able 71 // to observe all conf changes via Ready.CommittedEntries. 72 // 73 // TODO(bdarnell): These entries are still unstable; do we need to preserve 74 // the invariant that committed < unstable? 75 rn.raft.raftLog.committed = uint64(len(ents)) 76 for _, peer := range peers { 77 rn.raft.applyConfChange(pb.ConfChange{NodeID: peer.ID, Type: pb.ConfChangeAddNode}.AsV2()) 78 } 79 return nil 80 } 81