github.com/grafana/pyroscope@v1.18.0/cmd/profilecli/raft.go (about) 1 package main 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 8 "connectrpc.com/connect" 9 "google.golang.org/protobuf/encoding/protojson" 10 11 connectapi "github.com/grafana/pyroscope/pkg/api/connect" 12 "github.com/grafana/pyroscope/pkg/metastore/raftnode/raftnodepb" 13 "github.com/grafana/pyroscope/pkg/metastore/raftnode/raftnodepb/raftnodepbconnect" 14 ) 15 16 func (c *phlareClient) metadataOperatorClient() raftnodepbconnect.RaftNodeServiceClient { 17 return raftnodepbconnect.NewRaftNodeServiceClient( 18 c.httpClient(), 19 c.URL, 20 append( 21 connectapi.DefaultClientOptions(), 22 c.protocolOption(), 23 )..., 24 ) 25 } 26 27 type raftInfoParams struct { 28 *phlareClient 29 30 HumanFormat bool 31 } 32 33 func addRaftInfoParams(cmd commander) *raftInfoParams { 34 params := &raftInfoParams{} 35 params.phlareClient = addPhlareClient(cmd) 36 37 cmd.Flag("human", "Human readable output").Short('H').BoolVar(¶ms.HumanFormat) 38 39 return params 40 } 41 42 func raftInfo(ctx context.Context, params *raftInfoParams) error { 43 client := params.metadataOperatorClient() 44 45 res, err := client.NodeInfo(ctx, connect.NewRequest(&raftnodepb.NodeInfoRequest{})) 46 if err != nil { 47 return err 48 } 49 50 var s string 51 switch { 52 case params.HumanFormat: 53 s = formatHumanRaftInfo(res.Msg.Node) 54 default: 55 s, err = formatJSONRaftInfo(res.Msg.Node) 56 if err != nil { 57 return err 58 } 59 } 60 61 fmt.Println(s) 62 return nil 63 } 64 65 func formatHumanRaftInfo(node *raftnodepb.NodeInfo) string { 66 maxKeyPadding := func(keys []string) int { 67 max := 0 68 for _, k := range keys { 69 if len(k) > max { 70 max = len(k) 71 } 72 } 73 return max 74 } 75 76 appendPairs := func(sb *strings.Builder, pairs [][]string) { 77 keys := make([]string, 0, len(pairs)) 78 for _, pair := range pairs { 79 keys = append(keys, pair[0]) 80 } 81 82 keyPadding := maxKeyPadding(keys) 83 for _, pair := range pairs { 84 key, value := pair[0], pair[1] 85 fmt.Fprintf(sb, "%s:", key) 86 sb.WriteString(strings.Repeat(" ", keyPadding-len(key)+1)) 87 fmt.Fprintf(sb, "%s\n", value) 88 } 89 } 90 91 var sb strings.Builder 92 appendPairs(&sb, [][]string{ 93 {"ID", node.ServerId}, 94 {"Address", node.AdvertisedAddress}, 95 {"State", node.State}, 96 {"Leader ID", node.LeaderId}, 97 }) 98 99 sb.WriteString("Log:\n") 100 appendPairs(&sb, [][]string{ 101 {" Commit index", fmt.Sprint(node.CommitIndex)}, 102 {" Applied index", fmt.Sprint(node.AppliedIndex)}, 103 {" Last index", fmt.Sprint(node.LastIndex)}, 104 }) 105 106 sb.WriteString("Stats:\n") 107 for i := range node.Stats.Name { 108 appendPairs(&sb, [][]string{ 109 {" " + node.Stats.Name[i], node.Stats.Value[i]}, 110 }) 111 } 112 113 sb.WriteString("Peers:\n") 114 for _, peer := range node.Peers { 115 appendPairs(&sb, [][]string{ 116 {" ID", peer.ServerId}, 117 {" Address", peer.ServerAddress}, 118 {" Suffrage", peer.Suffrage}, 119 }) 120 sb.WriteString("\n") // Give some space between entries. 121 } 122 123 return strings.TrimSpace(sb.String()) 124 } 125 126 func formatJSONRaftInfo(node *raftnodepb.NodeInfo) (string, error) { 127 // Pretty print the protobuf json and don't omit default values. 128 opts := protojson.MarshalOptions{ 129 Multiline: true, 130 Indent: " ", 131 EmitUnpopulated: true, 132 } 133 134 bytes, err := opts.Marshal(node) 135 if err != nil { 136 return "", err 137 } 138 139 return string(bytes), nil 140 }