diff --git a/cmd/solana_exporter/exporter.go b/cmd/solana_exporter/exporter.go index 51b092a..b5273d3 100644 --- a/cmd/solana_exporter/exporter.go +++ b/cmd/solana_exporter/exporter.go @@ -20,6 +20,7 @@ const ( VersionLabel = "version" AddressLabel = "address" EpochLabel = "epoch" + IdentityLabel = "identity" StatusSkipped = "skipped" StatusValid = "valid" @@ -38,6 +39,7 @@ type SolanaCollector struct { // config: slotPace time.Duration balanceAddresses []string + identity *string /// descriptors: totalValidatorsDesc *prometheus.Desc @@ -51,13 +53,12 @@ type SolanaCollector struct { numSlotsBehind *prometheus.Desc } -func NewSolanaCollector( - provider rpc.Provider, slotPace time.Duration, balanceAddresses []string, nodekeys []string, votekeys []string, -) *SolanaCollector { +func NewSolanaCollector(provider rpc.Provider, slotPace time.Duration, balanceAddresses []string, nodekeys []string, votekeys []string, identity *string) *SolanaCollector { collector := &SolanaCollector{ rpcClient: provider, slotPace: slotPace, balanceAddresses: CombineUnique(balanceAddresses, nodekeys, votekeys), + identity: identity, totalValidatorsDesc: prometheus.NewDesc( "solana_active_validators", "Total number of active validators by state", @@ -103,13 +104,13 @@ func NewSolanaCollector( isHealthy: prometheus.NewDesc( "solana_is_healthy", "Whether the node is healthy or not.", - nil, + []string{IdentityLabel}, nil, ), numSlotsBehind: prometheus.NewDesc( "solana_num_slots_behind", "The number of slots that the node is behind the latest cluster confirmed slot.", - nil, + []string{IdentityLabel}, nil, ), } @@ -241,8 +242,8 @@ func (c *SolanaCollector) collectHealth(ctx context.Context, ch chan<- prometheu } } - ch <- prometheus.MustNewConstMetric(c.isHealthy, prometheus.GaugeValue, float64(isHealthy)) - ch <- prometheus.MustNewConstMetric(c.numSlotsBehind, prometheus.GaugeValue, float64(numSlotsBehind)) + ch <- prometheus.MustNewConstMetric(c.isHealthy, prometheus.GaugeValue, float64(isHealthy), *c.identity) + ch <- prometheus.MustNewConstMetric(c.numSlotsBehind, prometheus.GaugeValue, float64(numSlotsBehind), *c.identity) return } @@ -273,8 +274,11 @@ func main() { if err != nil { klog.Fatalf("Failed to get associated vote accounts for %v: %v", config.NodeKeys, err) } - - collector := NewSolanaCollector(client, slotPacerSchedule, config.BalanceAddresses, config.NodeKeys, votekeys) + identity, err := client.GetIdentity(ctx) + if err != nil { + klog.Fatalf("Failed to get identity: %v", err) + } + collector := NewSolanaCollector(client, slotPacerSchedule, config.BalanceAddresses, config.NodeKeys, votekeys, identity) slotWatcher := NewSlotWatcher( client, config.NodeKeys, votekeys, config.ComprehensiveSlotTracking, config.MonitorBlockSizes, ) diff --git a/cmd/solana_exporter/exporter_test.go b/cmd/solana_exporter/exporter_test.go index 38adb41..67ea6bc 100644 --- a/cmd/solana_exporter/exporter_test.go +++ b/cmd/solana_exporter/exporter_test.go @@ -43,6 +43,7 @@ type ( var ( identities = []string{"aaa", "bbb", "ccc"} votekeys = []string{"AAA", "BBB", "CCC"} + identity = "aaa" balances = map[string]float64{"aaa": 1, "bbb": 2, "ccc": 3, "AAA": 4, "BBB": 5, "CCC": 6} identityVotes = map[string]string{"aaa": "AAA", "bbb": "BBB", "ccc": "CCC"} nv = len(identities) @@ -420,7 +421,7 @@ func runCollectionTests(t *testing.T, collector prometheus.Collector, testCases } func TestSolanaCollector_Collect_Static(t *testing.T) { - collector := NewSolanaCollector(&staticRPCClient{}, slotPacerSchedule, nil, identities, votekeys) + collector := NewSolanaCollector(&staticRPCClient{}, slotPacerSchedule, nil, identities, votekeys, &identity) prometheus.NewPedanticRegistry().MustRegister(collector) testCases := []collectionTest{ @@ -492,7 +493,7 @@ solana_node_version{version="1.16.7"} 1 func TestSolanaCollector_Collect_Dynamic(t *testing.T) { client := newDynamicRPCClient() - collector := NewSolanaCollector(client, slotPacerSchedule, nil, identities, votekeys) + collector := NewSolanaCollector(client, slotPacerSchedule, nil, identities, votekeys, &identity) prometheus.NewPedanticRegistry().MustRegister(collector) // start off by testing initial state: diff --git a/pkg/rpc/client.go b/pkg/rpc/client.go index 8fcff76..28ceb23 100644 --- a/pkg/rpc/client.go +++ b/pkg/rpc/client.go @@ -75,6 +75,7 @@ type Provider interface { GetBlock(ctx context.Context, commitment Commitment, slot int64, transactionDetails string) (*Block, error) GetHealth(ctx context.Context) (*string, error) + GetIdentity(ctx context.Context) (*string, error) } func (c Commitment) MarshalJSON() ([]byte, error) { @@ -315,3 +316,13 @@ func (c *Client) GetHealth(ctx context.Context) (*string, error) { } return &resp.Result, nil } + +// GetIdentity Returns the identity pubkey for the current node +// See API docs: https://solana.com/docs/rpc/http/getidentity +func (c *Client) GetIdentity(ctx context.Context) (*string, error) { + var resp response[Identity] + if err := getResponse(ctx, c, "getIdentity", []any{}, &resp); err != nil { + return nil, err + } + return &resp.Result.Identity, nil +} diff --git a/pkg/rpc/responses.go b/pkg/rpc/responses.go index f587f22..6a23180 100644 --- a/pkg/rpc/responses.go +++ b/pkg/rpc/responses.go @@ -98,6 +98,9 @@ type ( RewardType string `json:"rewardType"` Commission uint8 `json:"commission"` } + Identity struct { + Identity string `json:"identity"` + } ) func (e *RPCError) Error() string {