Scott Baker | 611f6bd | 2019-10-18 13:45:19 -0700 | [diff] [blame] | 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/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 | } |