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
15package raft
16
17import (
18	"fmt"
19
20	pb "go.etcd.io/etcd/raft/raftpb"
21)
22
23type Status struct {
24	ID uint64
25
26	pb.HardState
27	SoftState
28
29	Applied  uint64
30	Progress map[uint64]Progress
31
32	LeadTransferee uint64
33}
34
35func getProgressCopy(r *raft) map[uint64]Progress {
36	prs := make(map[uint64]Progress)
37	for id, p := range r.prs {
38		prs[id] = *p
39	}
40
41	for id, p := range r.learnerPrs {
42		prs[id] = *p
43	}
44	return prs
45}
46
47func getStatusWithoutProgress(r *raft) Status {
48	s := Status{
49		ID:             r.id,
50		LeadTransferee: r.leadTransferee,
51	}
52	s.HardState = r.hardState()
53	s.SoftState = *r.softState()
54	s.Applied = r.raftLog.applied
55	return s
56}
57
58// getStatus gets a copy of the current raft status.
59func getStatus(r *raft) Status {
60	s := getStatusWithoutProgress(r)
61	if s.RaftState == StateLeader {
62		s.Progress = getProgressCopy(r)
63	}
64	return s
65}
66
67// MarshalJSON translates the raft status into JSON.
68// TODO: try to simplify this by introducing ID type into raft
69func (s Status) MarshalJSON() ([]byte, error) {
70	j := fmt.Sprintf(`{"id":"%x","term":%d,"vote":"%x","commit":%d,"lead":"%x","raftState":%q,"applied":%d,"progress":{`,
71		s.ID, s.Term, s.Vote, s.Commit, s.Lead, s.RaftState, s.Applied)
72
73	if len(s.Progress) == 0 {
74		j += "},"
75	} else {
76		for k, v := range s.Progress {
77			subj := fmt.Sprintf(`"%x":{"match":%d,"next":%d,"state":%q},`, k, v.Match, v.Next, v.State)
78			j += subj
79		}
80		// remove the trailing ","
81		j = j[:len(j)-1] + "},"
82	}
83
84	j += fmt.Sprintf(`"leadtransferee":"%x"}`, s.LeadTransferee)
85	return []byte(j), nil
86}
87
88func (s Status) String() string {
89	b, err := s.MarshalJSON()
90	if err != nil {
91		raftLogger.Panicf("unexpected error: %v", err)
92	}
93	return string(b)
94}
95