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(&params.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  }