blob: bd82b2041af766d8d9e1bdf500bd1a69e303a12b [file] [log] [blame]
David Bainbridge788e5202019-10-21 18:49:40 +00001// 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
15package raft
16
17import (
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.
30func (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}