Skip to content

Commit c5074d2

Browse files
committed
connectrpc health api
Signed-off-by: Joshua Kim <[email protected]>
1 parent 0fc4e2c commit c5074d2

File tree

34 files changed

+7760
-155
lines changed

34 files changed

+7760
-155
lines changed
Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
package connecthandler
5+
6+
import (
7+
"context"
8+
"encoding/json"
9+
"fmt"
10+
11+
"connectrpc.com/connect"
12+
13+
"github.com/ava-labs/avalanchego/api"
14+
"github.com/ava-labs/avalanchego/api/admin"
15+
"github.com/ava-labs/avalanchego/proto/pb/admin/v1/adminv1connect"
16+
"github.com/ava-labs/avalanchego/utils/logging"
17+
18+
adminv1 "github.com/ava-labs/avalanchego/proto/pb/admin/v1"
19+
)
20+
21+
var _ adminv1connect.AdminServiceHandler = (*connectAdminService)(nil)
22+
23+
type connectAdminService struct {
24+
admin *admin.Admin
25+
}
26+
27+
// NewConnectAdminService returns a ConnectRPC handler for the Admin API
28+
func NewConnectAdminService(admin *admin.Admin) adminv1connect.AdminServiceHandler {
29+
return &connectAdminService{
30+
admin: admin,
31+
}
32+
}
33+
34+
// StartCPUProfiler starts a CPU profile writing to the specified file
35+
func (c *connectAdminService) StartCPUProfiler(
36+
_ context.Context,
37+
_ *connect.Request[adminv1.StartCPUProfilerRequest],
38+
) (*connect.Response[adminv1.StartCPUProfilerResponse], error) {
39+
if err := c.admin.StartCPUProfiler(nil, nil, &api.EmptyReply{}); err != nil {
40+
return nil, err
41+
}
42+
43+
return connect.NewResponse(&adminv1.StartCPUProfilerResponse{}), nil
44+
}
45+
46+
// StopCPUProfiler stops the CPU profile
47+
func (c *connectAdminService) StopCPUProfiler(
48+
_ context.Context,
49+
_ *connect.Request[adminv1.StopCPUProfilerRequest],
50+
) (*connect.Response[adminv1.StopCPUProfilerResponse], error) {
51+
if err := c.admin.StopCPUProfiler(nil, nil, &api.EmptyReply{}); err != nil {
52+
return nil, err
53+
}
54+
55+
return connect.NewResponse(&adminv1.StopCPUProfilerResponse{}), nil
56+
}
57+
58+
// MemoryProfile runs a memory profile writing to the specified file
59+
func (c *connectAdminService) MemoryProfile(
60+
_ context.Context,
61+
_ *connect.Request[adminv1.MemoryProfileRequest],
62+
) (*connect.Response[adminv1.MemoryProfileResponse], error) {
63+
if err := c.admin.MemoryProfile(nil, nil, &api.EmptyReply{}); err != nil {
64+
return nil, err
65+
}
66+
67+
return connect.NewResponse(&adminv1.MemoryProfileResponse{}), nil
68+
}
69+
70+
// LockProfile runs a lock profile writing to the specified file
71+
func (c *connectAdminService) LockProfile(
72+
_ context.Context,
73+
_ *connect.Request[adminv1.LockProfileRequest],
74+
) (*connect.Response[adminv1.LockProfileResponse], error) {
75+
if err := c.admin.LockProfile(nil, nil, &api.EmptyReply{}); err != nil {
76+
return nil, err
77+
}
78+
79+
return connect.NewResponse(&adminv1.LockProfileResponse{}), nil
80+
}
81+
82+
// Alias attempts to alias an HTTP endpoint to a new name
83+
func (c *connectAdminService) Alias(
84+
_ context.Context,
85+
request *connect.Request[adminv1.AliasRequest],
86+
) (*connect.Response[adminv1.AliasResponse], error) {
87+
jsonRequest := &admin.AliasArgs{
88+
Endpoint: request.Msg.Endpoint,
89+
Alias: request.Msg.Alias,
90+
}
91+
92+
if err := c.admin.Alias(nil, jsonRequest, &api.EmptyReply{}); err != nil {
93+
return nil, err
94+
}
95+
96+
return connect.NewResponse(&adminv1.AliasResponse{}), nil
97+
}
98+
99+
// AliasChain attempts to alias a chain to a new name
100+
func (c *connectAdminService) AliasChain(
101+
_ context.Context,
102+
request *connect.Request[adminv1.AliasChainRequest],
103+
) (*connect.Response[adminv1.AliasChainResponse], error) {
104+
jsonRequest := &admin.AliasChainArgs{
105+
Chain: request.Msg.Chain,
106+
Alias: request.Msg.Alias,
107+
}
108+
109+
if err := c.admin.AliasChain(nil, jsonRequest, &api.EmptyReply{}); err != nil {
110+
return nil, err
111+
}
112+
113+
return connect.NewResponse(&adminv1.AliasChainResponse{}), nil
114+
}
115+
116+
// GetChainAliases returns the aliases of the chain
117+
func (c *connectAdminService) GetChainAliases(
118+
_ context.Context,
119+
request *connect.Request[adminv1.GetChainAliasesRequest],
120+
) (*connect.Response[adminv1.GetChainAliasesResponse], error) {
121+
jsonRequest := &admin.GetChainAliasesArgs{
122+
Chain: request.Msg.Chain,
123+
}
124+
125+
var jsonResponse admin.GetChainAliasesReply
126+
if err := c.admin.GetChainAliases(nil, jsonRequest, &jsonResponse); err != nil {
127+
return nil, err
128+
}
129+
130+
Response := &adminv1.GetChainAliasesResponse{
131+
Aliases: jsonResponse.Aliases,
132+
}
133+
134+
return connect.NewResponse(Response), nil
135+
}
136+
137+
// Stacktrace returns the current global stacktrace
138+
func (c *connectAdminService) Stacktrace(
139+
_ context.Context,
140+
_ *connect.Request[adminv1.StacktraceRequest],
141+
) (*connect.Response[adminv1.StacktraceResponse], error) {
142+
if err := c.admin.Stacktrace(nil, nil, &api.EmptyReply{}); err != nil {
143+
return nil, err
144+
}
145+
146+
return connect.NewResponse(&adminv1.StacktraceResponse{}), nil
147+
}
148+
149+
// SetLoggerLevel sets the log level and/or display level for loggers
150+
func (c *connectAdminService) SetLoggerLevel(
151+
_ context.Context,
152+
request *connect.Request[adminv1.SetLoggerLevelRequest],
153+
) (*connect.Response[adminv1.SetLoggerLevelResponse], error) {
154+
jsonRequest := &admin.SetLoggerLevelArgs{
155+
LoggerName: request.Msg.LoggerName,
156+
}
157+
158+
if request.Msg.LogLevel != "" {
159+
logLevel, err := logging.ToLevel(request.Msg.LogLevel)
160+
if err != nil {
161+
return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("invalid log level: %w", err))
162+
}
163+
jsonRequest.LogLevel = &logLevel
164+
}
165+
166+
if request.Msg.DisplayLevel != "" {
167+
displayLevel, err := logging.ToLevel(request.Msg.DisplayLevel)
168+
if err != nil {
169+
return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("invalid display level: %w", err))
170+
}
171+
jsonRequest.DisplayLevel = &displayLevel
172+
}
173+
174+
var jsonResponse admin.LoggerLevelReply
175+
if err := c.admin.SetLoggerLevel(nil, jsonRequest, &jsonResponse); err != nil {
176+
return nil, err
177+
}
178+
179+
protoLoggerLevels := make(map[string]*adminv1.LogAndDisplayLevels)
180+
181+
for name, levels := range jsonResponse.LoggerLevels {
182+
protoLoggerLevels[name] = &adminv1.LogAndDisplayLevels{
183+
LogLevel: levels.LogLevel.String(),
184+
DisplayLevel: levels.DisplayLevel.String(),
185+
}
186+
}
187+
188+
return connect.NewResponse(&adminv1.SetLoggerLevelResponse{
189+
LoggerLevels: protoLoggerLevels,
190+
}), nil
191+
}
192+
193+
// GetLoggerLevel returns the log level and display level of all loggers
194+
func (c *connectAdminService) GetLoggerLevel(
195+
_ context.Context,
196+
request *connect.Request[adminv1.GetLoggerLevelRequest],
197+
) (*connect.Response[adminv1.GetLoggerLevelResponse], error) {
198+
jsonRequest := &admin.GetLoggerLevelArgs{
199+
LoggerName: request.Msg.LoggerName,
200+
}
201+
202+
var jsonResponse admin.LoggerLevelReply
203+
if err := c.admin.GetLoggerLevel(nil, jsonRequest, &jsonResponse); err != nil {
204+
return nil, err
205+
}
206+
207+
protoLoggerLevels := make(map[string]*adminv1.LogAndDisplayLevels)
208+
209+
for name, levels := range jsonResponse.LoggerLevels {
210+
protoLoggerLevels[name] = &adminv1.LogAndDisplayLevels{
211+
LogLevel: levels.LogLevel.String(),
212+
DisplayLevel: levels.DisplayLevel.String(),
213+
}
214+
}
215+
216+
return connect.NewResponse(&adminv1.GetLoggerLevelResponse{
217+
LoggerLevels: protoLoggerLevels,
218+
}), nil
219+
}
220+
221+
// GetConfig returns the config that the node was started with
222+
func (c *connectAdminService) GetConfig(
223+
_ context.Context,
224+
_ *connect.Request[adminv1.GetConfigRequest],
225+
) (*connect.Response[adminv1.GetConfigResponse], error) {
226+
var jsonResponse interface{}
227+
if err := c.admin.GetConfig(nil, nil, &jsonResponse); err != nil {
228+
return nil, err
229+
}
230+
231+
// Convert the config to JSON
232+
configJSON, err := json.Marshal(jsonResponse)
233+
if err != nil {
234+
return nil, err
235+
}
236+
237+
Response := &adminv1.GetConfigResponse{
238+
ConfigJson: string(configJSON),
239+
}
240+
241+
return connect.NewResponse(Response), nil
242+
}
243+
244+
// DBGet returns the value of a database entry
245+
func (c *connectAdminService) DBGet(
246+
_ context.Context,
247+
request *connect.Request[adminv1.DBGetRequest],
248+
) (*connect.Response[adminv1.DBGetResponse], error) {
249+
jsonRequest := &admin.DBGetArgs{
250+
Key: request.Msg.Key,
251+
}
252+
253+
var jsonResponse admin.DBGetReply
254+
if err := c.admin.DbGet(nil, jsonRequest, &jsonResponse); err != nil {
255+
return nil, err
256+
}
257+
258+
Response := &adminv1.DBGetResponse{
259+
Value: jsonResponse.Value,
260+
ErrorCode: adminv1.ErrorCode(jsonResponse.ErrorCode),
261+
}
262+
263+
return connect.NewResponse(Response), nil
264+
}

api/admin/service.go

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,21 +59,18 @@ type Config struct {
5959
type Admin struct {
6060
Config
6161
lock sync.RWMutex
62-
profiler profiler.Profiler
62+
Profiler profiler.Profiler
6363
}
6464

6565
// NewService returns a new admin API service.
6666
// All of the fields in [config] must be set.
67-
func NewService(config Config) (http.Handler, error) {
67+
func NewService(admin *Admin) (http.Handler, error) {
6868
server := rpc.NewServer()
6969
codec := json.NewCodec()
7070
server.RegisterCodec(codec, "application/json")
7171
server.RegisterCodec(codec, "application/json;charset=UTF-8")
7272
return server, server.RegisterService(
73-
&Admin{
74-
Config: config,
75-
profiler: profiler.New(config.ProfileDir),
76-
},
73+
admin,
7774
"admin",
7875
)
7976
}
@@ -88,7 +85,7 @@ func (a *Admin) StartCPUProfiler(_ *http.Request, _ *struct{}, _ *api.EmptyReply
8885
a.lock.Lock()
8986
defer a.lock.Unlock()
9087

91-
return a.profiler.StartCPUProfiler()
88+
return a.Profiler.StartCPUProfiler()
9289
}
9390

9491
// StopCPUProfiler stops the cpu profile
@@ -101,7 +98,7 @@ func (a *Admin) StopCPUProfiler(_ *http.Request, _ *struct{}, _ *api.EmptyReply)
10198
a.lock.Lock()
10299
defer a.lock.Unlock()
103100

104-
return a.profiler.StopCPUProfiler()
101+
return a.Profiler.StopCPUProfiler()
105102
}
106103

107104
// MemoryProfile runs a memory profile writing to the specified file
@@ -114,7 +111,7 @@ func (a *Admin) MemoryProfile(_ *http.Request, _ *struct{}, _ *api.EmptyReply) e
114111
a.lock.Lock()
115112
defer a.lock.Unlock()
116113

117-
return a.profiler.MemoryProfile()
114+
return a.Profiler.MemoryProfile()
118115
}
119116

120117
// LockProfile runs a mutex profile writing to the specified file
@@ -127,7 +124,7 @@ func (a *Admin) LockProfile(_ *http.Request, _ *struct{}, _ *api.EmptyReply) err
127124
a.lock.Lock()
128125
defer a.lock.Unlock()
129126

130-
return a.profiler.LockProfile()
127+
return a.Profiler.LockProfile()
131128
}
132129

133130
// AliasArgs are the arguments for calling Alias
@@ -391,7 +388,7 @@ type DBGetReply struct {
391388
ErrorCode rpcdbpb.Error `json:"errorCode"`
392389
}
393390

394-
//nolint:staticcheck // renaming this method to DBGet would change the API method from "dbGet" to "dBGet"
391+
//nolint:stylecheck // renaming this method to DBGet would change the API method from "dbGet" to "dBGet"
395392
func (a *Admin) DbGet(_ *http.Request, args *DBGetArgs, reply *DBGetReply) error {
396393
a.Log.Debug("API called",
397394
zap.String("service", "admin"),

0 commit comments

Comments
 (0)