node/cmd: unary list-nodes call with details
Change-Id: I9953b45d92461887b075b3456bdd9e161eefd263
This commit is contained in:
parent
336f373bb5
commit
61dd1c1052
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
publicrpcv1 "github.com/certusone/wormhole/bridge/pkg/proto/publicrpc/v1"
|
publicrpcv1 "github.com/certusone/wormhole/bridge/pkg/proto/publicrpc/v1"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
@ -16,25 +17,27 @@ import (
|
||||||
nodev1 "github.com/certusone/wormhole/bridge/pkg/proto/node/v1"
|
nodev1 "github.com/certusone/wormhole/bridge/pkg/proto/node/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var clientSocketPath *string
|
var (
|
||||||
|
clientSocketPath *string
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
pf := AdminClientInjectGuardianSetUpdateCmd.Flags()
|
// Shared flags for all admin commands
|
||||||
|
pf := pflag.NewFlagSet("commonAdminFlags", pflag.ContinueOnError)
|
||||||
clientSocketPath = pf.String("socket", "", "gRPC admin server socket to connect to")
|
clientSocketPath = pf.String("socket", "", "gRPC admin server socket to connect to")
|
||||||
err := cobra.MarkFlagRequired(pf, "socket")
|
err := cobra.MarkFlagRequired(pf, "socket")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
pf = AdminClientListNodesStream.Flags()
|
|
||||||
clientSocketPath = pf.String("socket", "", "gRPC admin server socket to connect to")
|
AdminClientInjectGuardianSetUpdateCmd.Flags().AddFlagSet(pf)
|
||||||
err = cobra.MarkFlagRequired(pf, "socket")
|
AdminClientListNodesStream.Flags().AddFlagSet(pf)
|
||||||
if err != nil {
|
AdminClientListNodes.Flags().AddFlagSet(pf)
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
AdminCmd.AddCommand(AdminClientInjectGuardianSetUpdateCmd)
|
AdminCmd.AddCommand(AdminClientInjectGuardianSetUpdateCmd)
|
||||||
AdminCmd.AddCommand(AdminClientGovernanceVAAVerifyCmd)
|
AdminCmd.AddCommand(AdminClientGovernanceVAAVerifyCmd)
|
||||||
AdminCmd.AddCommand(AdminClientListNodesStream)
|
AdminCmd.AddCommand(AdminClientListNodesStream)
|
||||||
|
AdminCmd.AddCommand(AdminClientListNodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
var AdminCmd = &cobra.Command{
|
var AdminCmd = &cobra.Command{
|
||||||
|
|
|
@ -3,12 +3,16 @@ package guardiand
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
gossipv1 "github.com/certusone/wormhole/bridge/pkg/proto/gossip/v1"
|
||||||
publicrpcv1 "github.com/certusone/wormhole/bridge/pkg/proto/publicrpc/v1"
|
publicrpcv1 "github.com/certusone/wormhole/bridge/pkg/proto/publicrpc/v1"
|
||||||
|
"github.com/certusone/wormhole/bridge/pkg/vaa"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// How to test in container:
|
// How to test in container:
|
||||||
|
@ -55,3 +59,63 @@ func runListNodesStream(cmd *cobra.Command, args []string) {
|
||||||
seen[hb.GuardianAddr] = true
|
seen[hb.GuardianAddr] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var AdminClientListNodes = &cobra.Command{
|
||||||
|
Use: "list-nodes",
|
||||||
|
Short: "Fetches an aggregated list of guardian nodes",
|
||||||
|
Run: runListNodes,
|
||||||
|
}
|
||||||
|
|
||||||
|
func runListNodes(cmd *cobra.Command, args []string) {
|
||||||
|
ctx := context.Background()
|
||||||
|
conn, err, c := getPublicrpcClient(ctx, *clientSocketPath)
|
||||||
|
defer conn.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to get publicrpc client: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
lastHeartbeats, err := c.GetLastHeartbeats(ctx, &publicrpcv1.GetLastHeartbeatRequest{})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to list nodes: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes := make([]*gossipv1.Heartbeat, len(lastHeartbeats.RawHeartbeats))
|
||||||
|
i := 0
|
||||||
|
for _, v := range lastHeartbeats.RawHeartbeats {
|
||||||
|
nodes[i] = v
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
sort.Slice(nodes, func(i, j int) bool {
|
||||||
|
return nodes[i].NodeName < nodes[j].NodeName
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Printf("%d nodes in guardian state set", len(nodes))
|
||||||
|
|
||||||
|
w := tabwriter.NewWriter(os.Stdout, 0, 8, 2, ' ', 0)
|
||||||
|
|
||||||
|
w.Write([]byte("Guardian key\tNode name\tVersion\tLast seen\tUptime\tSolana\tEthereum\tTerra\tBSC\n"))
|
||||||
|
|
||||||
|
for _, h := range nodes {
|
||||||
|
last := time.Unix(0, h.Timestamp)
|
||||||
|
|
||||||
|
heights := map[vaa.ChainID]int64{}
|
||||||
|
for _, n := range h.Networks {
|
||||||
|
heights[vaa.ChainID(n.Id)] = n.Height
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(w,
|
||||||
|
"%s\t%s\t%s\t%s\t%d\t%d\t%d\t%d\t%d\n",
|
||||||
|
h.GuardianAddr,
|
||||||
|
h.NodeName,
|
||||||
|
h.Version,
|
||||||
|
time.Since(last),
|
||||||
|
h.Counter,
|
||||||
|
heights[vaa.ChainIDSolana],
|
||||||
|
heights[vaa.ChainIDEthereum],
|
||||||
|
heights[vaa.ChainIDTerra],
|
||||||
|
heights[vaa.ChainIDBSC],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Flush()
|
||||||
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ require (
|
||||||
github.com/near/borsh-go v0.3.0
|
github.com/near/borsh-go v0.3.0
|
||||||
github.com/prometheus/client_golang v1.10.0
|
github.com/prometheus/client_golang v1.10.0
|
||||||
github.com/spf13/cobra v1.1.1
|
github.com/spf13/cobra v1.1.1
|
||||||
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/spf13/viper v1.7.1
|
github.com/spf13/viper v1.7.1
|
||||||
github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969
|
github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
|
|
|
@ -47,11 +47,19 @@ func (s *PublicrpcServer) GetLastHeartbeats(ctx context.Context, req *publicrpcv
|
||||||
RawHeartbeats: make(map[string]*gossipv1.Heartbeat),
|
RawHeartbeats: make(map[string]*gossipv1.Heartbeat),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Request heartbeat for every guardian set entry. This ensures that
|
||||||
|
// offline guardians will be listed with a null heartbeat.
|
||||||
for _, addr := range gs.Keys {
|
for _, addr := range gs.Keys {
|
||||||
hb := s.gst.LastHeartbeat(addr)
|
hb := s.gst.LastHeartbeat(addr)
|
||||||
resp.RawHeartbeats[addr.Hex()] = hb
|
resp.RawHeartbeats[addr.Hex()] = hb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch all heartbeats (including from nodes not in the guardian set - which
|
||||||
|
// can happen either with --disableHeartbeatVerify or when the guardian set changes)
|
||||||
|
for addr, hb := range s.gst.GetAll() {
|
||||||
|
resp.RawHeartbeats[addr.Hex()] = hb
|
||||||
|
}
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue