mirror of
https://github.com/cwinfo/yggdrasil-go.git
synced 2024-11-21 20:40:28 +00:00
Admin socket and yggdrasilctl
improvements
This refactors the request parsing, as well as improving the output for some request types. It also tweaks `yggdrasilctl` output, which should help with #947.
This commit is contained in:
parent
5ef61faeff
commit
b67c313f44
@ -16,6 +16,8 @@ import (
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/admin"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/multicast"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/tuntap"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/version"
|
||||
)
|
||||
|
||||
@ -135,6 +137,7 @@ func run() int {
|
||||
table.SetBorder(false)
|
||||
table.SetTablePadding("\t") // pad with tabs
|
||||
table.SetNoWhiteSpace(true)
|
||||
table.SetAutoWrapText(false)
|
||||
|
||||
switch strings.ToLower(recv.Request.Name) {
|
||||
case "list":
|
||||
@ -142,9 +145,12 @@ func run() int {
|
||||
if err := json.Unmarshal(recv.Response, &resp); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
table.SetHeader([]string{"Command", "Arguments"})
|
||||
table.SetHeader([]string{"Command", "Arguments", "Description"})
|
||||
for _, entry := range resp.List {
|
||||
table.Append([]string{entry.Command, strings.Join(entry.Fields, ", ")})
|
||||
for i := range entry.Fields {
|
||||
entry.Fields[i] = entry.Fields[i] + "=..."
|
||||
}
|
||||
table.Append([]string{entry.Command, strings.Join(entry.Fields, ", "), entry.Description})
|
||||
}
|
||||
table.Render()
|
||||
|
||||
@ -238,8 +244,31 @@ func run() int {
|
||||
break
|
||||
}
|
||||
|
||||
case "getmulticastinterfaces":
|
||||
var resp multicast.GetMulticastInterfacesResponse
|
||||
if err := json.Unmarshal(recv.Response, &resp); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
table.SetHeader([]string{"Interface"})
|
||||
for _, p := range resp.Interfaces {
|
||||
table.Append([]string{p})
|
||||
}
|
||||
table.Render()
|
||||
|
||||
case "gettun":
|
||||
var resp tuntap.GetTUNResponse
|
||||
if err := json.Unmarshal(recv.Response, &resp); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
table.Append([]string{"TUN enabled:", fmt.Sprintf("%#v", resp.Enabled)})
|
||||
if resp.Enabled {
|
||||
table.Append([]string{"Interface name:", resp.Name})
|
||||
table.Append([]string{"Interface MTU:", fmt.Sprintf("%d", resp.MTU)})
|
||||
}
|
||||
table.Render()
|
||||
|
||||
default:
|
||||
panic("unknown response type: " + recv.Request.Name)
|
||||
fmt.Println(string(recv.Response))
|
||||
}
|
||||
|
||||
return 0
|
||||
|
@ -43,6 +43,7 @@ type AdminSocketResponse struct {
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
desc string // What does the endpoint do?
|
||||
args []string // List of human-readable argument names
|
||||
handler core.AddHandlerFunc // First is input map, second is output
|
||||
}
|
||||
@ -52,16 +53,18 @@ type ListResponse struct {
|
||||
}
|
||||
|
||||
type ListEntry struct {
|
||||
Command string `json:"command"`
|
||||
Fields []string `json:"fields,omitempty"`
|
||||
Command string `json:"command"`
|
||||
Description string `json:"description"`
|
||||
Fields []string `json:"fields,omitempty"`
|
||||
}
|
||||
|
||||
// AddHandler is called for each admin function to add the handler and help documentation to the API.
|
||||
func (a *AdminSocket) AddHandler(name string, args []string, handlerfunc core.AddHandlerFunc) error {
|
||||
func (a *AdminSocket) AddHandler(name, desc string, args []string, handlerfunc core.AddHandlerFunc) error {
|
||||
if _, ok := a.handlers[strings.ToLower(name)]; ok {
|
||||
return errors.New("handler already exists")
|
||||
}
|
||||
a.handlers[strings.ToLower(name)] = handler{
|
||||
desc: desc,
|
||||
args: args,
|
||||
handler: handlerfunc,
|
||||
}
|
||||
@ -81,12 +84,13 @@ func New(c *core.Core, log util.Logger, opts ...SetupOption) (*AdminSocket, erro
|
||||
if a.config.listenaddr == "none" || a.config.listenaddr == "" {
|
||||
return nil, nil
|
||||
}
|
||||
_ = a.AddHandler("list", []string{}, func(_ json.RawMessage) (interface{}, error) {
|
||||
_ = a.AddHandler("list", "List available commands", []string{}, func(_ json.RawMessage) (interface{}, error) {
|
||||
res := &ListResponse{}
|
||||
for name, handler := range a.handlers {
|
||||
res.List = append(res.List, ListEntry{
|
||||
Command: name,
|
||||
Fields: handler.args,
|
||||
Command: name,
|
||||
Description: handler.desc,
|
||||
Fields: handler.args,
|
||||
})
|
||||
}
|
||||
sort.SliceStable(res.List, func(i, j int) bool {
|
||||
@ -100,61 +104,76 @@ func New(c *core.Core, log util.Logger, opts ...SetupOption) (*AdminSocket, erro
|
||||
}
|
||||
|
||||
func (a *AdminSocket) SetupAdminHandlers() {
|
||||
_ = a.AddHandler("getSelf", []string{}, func(in json.RawMessage) (interface{}, error) {
|
||||
req := &GetSelfRequest{}
|
||||
res := &GetSelfResponse{}
|
||||
if err := json.Unmarshal(in, &req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := a.getSelfHandler(req, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
})
|
||||
_ = a.AddHandler("getPeers", []string{}, func(in json.RawMessage) (interface{}, error) {
|
||||
req := &GetPeersRequest{}
|
||||
res := &GetPeersResponse{}
|
||||
if err := json.Unmarshal(in, &req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := a.getPeersHandler(req, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
})
|
||||
_ = a.AddHandler("getDHT", []string{}, func(in json.RawMessage) (interface{}, error) {
|
||||
req := &GetDHTRequest{}
|
||||
res := &GetDHTResponse{}
|
||||
if err := json.Unmarshal(in, &req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := a.getDHTHandler(req, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
})
|
||||
_ = a.AddHandler("getPaths", []string{}, func(in json.RawMessage) (interface{}, error) {
|
||||
req := &GetPathsRequest{}
|
||||
res := &GetPathsResponse{}
|
||||
if err := json.Unmarshal(in, &req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := a.getPathsHandler(req, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
})
|
||||
_ = a.AddHandler("getSessions", []string{}, func(in json.RawMessage) (interface{}, error) {
|
||||
req := &GetSessionsRequest{}
|
||||
res := &GetSessionsResponse{}
|
||||
if err := json.Unmarshal(in, &req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := a.getSessionsHandler(req, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
})
|
||||
_ = a.AddHandler(
|
||||
"getSelf", "Show details about this node", []string{},
|
||||
func(in json.RawMessage) (interface{}, error) {
|
||||
req := &GetSelfRequest{}
|
||||
res := &GetSelfResponse{}
|
||||
if err := json.Unmarshal(in, &req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := a.getSelfHandler(req, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
},
|
||||
)
|
||||
_ = a.AddHandler(
|
||||
"getPeers", "Show directly connected peers", []string{},
|
||||
func(in json.RawMessage) (interface{}, error) {
|
||||
req := &GetPeersRequest{}
|
||||
res := &GetPeersResponse{}
|
||||
if err := json.Unmarshal(in, &req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := a.getPeersHandler(req, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
},
|
||||
)
|
||||
_ = a.AddHandler(
|
||||
"getDHT", "Show known DHT entries", []string{},
|
||||
func(in json.RawMessage) (interface{}, error) {
|
||||
req := &GetDHTRequest{}
|
||||
res := &GetDHTResponse{}
|
||||
if err := json.Unmarshal(in, &req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := a.getDHTHandler(req, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
},
|
||||
)
|
||||
_ = a.AddHandler(
|
||||
"getPaths", "Show established paths through this node", []string{},
|
||||
func(in json.RawMessage) (interface{}, error) {
|
||||
req := &GetPathsRequest{}
|
||||
res := &GetPathsResponse{}
|
||||
if err := json.Unmarshal(in, &req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := a.getPathsHandler(req, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
},
|
||||
)
|
||||
_ = a.AddHandler(
|
||||
"getSessions", "Show established traffic sessions with remote nodes", []string{},
|
||||
func(in json.RawMessage) (interface{}, error) {
|
||||
req := &GetSessionsRequest{}
|
||||
res := &GetSessionsResponse{}
|
||||
if err := json.Unmarshal(in, &req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := a.getSessionsHandler(req, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
},
|
||||
)
|
||||
//_ = a.AddHandler("getNodeInfo", []string{"key"}, t.proto.nodeinfo.nodeInfoAdminHandler)
|
||||
//_ = a.AddHandler("debug_remoteGetSelf", []string{"key"}, t.proto.getSelfHandler)
|
||||
//_ = a.AddHandler("debug_remoteGetPeers", []string{"key"}, t.proto.getPeersHandler)
|
||||
@ -279,35 +298,34 @@ func (a *AdminSocket) handleRequest(conn net.Conn) {
|
||||
for {
|
||||
var err error
|
||||
var buf json.RawMessage
|
||||
_ = decoder.Decode(&buf)
|
||||
var resp AdminSocketResponse
|
||||
resp.Status = "success"
|
||||
if err = json.Unmarshal(buf, &resp.Request); err == nil {
|
||||
if resp.Request.Name == "" {
|
||||
resp.Status = "error"
|
||||
resp.Response, _ = json.Marshal(ErrorResponse{
|
||||
Error: "No request specified",
|
||||
})
|
||||
} else if h, ok := a.handlers[strings.ToLower(resp.Request.Name)]; ok {
|
||||
res, err := h.handler(buf)
|
||||
if err != nil {
|
||||
resp.Status = "error"
|
||||
resp.Response, _ = json.Marshal(ErrorResponse{
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
if resp.Response, err = json.Marshal(res); err != nil {
|
||||
resp.Status = "error"
|
||||
resp.Response, _ = json.Marshal(ErrorResponse{
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
resp.Status = "error"
|
||||
resp.Response, _ = json.Marshal(ErrorResponse{
|
||||
Error: fmt.Sprintf("Unknown action '%s', try 'list' for help", resp.Request.Name),
|
||||
})
|
||||
if err := func() error {
|
||||
if err = decoder.Decode(&buf); err != nil {
|
||||
return fmt.Errorf("Failed to find request")
|
||||
}
|
||||
if err = json.Unmarshal(buf, &resp.Request); err != nil {
|
||||
return fmt.Errorf("Failed to unmarshal request")
|
||||
}
|
||||
if resp.Request.Name == "" {
|
||||
return fmt.Errorf("No request specified")
|
||||
}
|
||||
reqname := strings.ToLower(resp.Request.Name)
|
||||
handler, ok := a.handlers[reqname]
|
||||
if !ok {
|
||||
return fmt.Errorf("Unknown action '%s', try 'list' for help", reqname)
|
||||
}
|
||||
res, err := handler.handler(buf)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Handler returned error: %w", err)
|
||||
}
|
||||
if resp.Response, err = json.Marshal(res); err != nil {
|
||||
return fmt.Errorf("Failed to marshal response: %w", err)
|
||||
}
|
||||
resp.Status = "success"
|
||||
return nil
|
||||
}(); err != nil {
|
||||
resp.Status = "error"
|
||||
resp.Error = err.Error()
|
||||
}
|
||||
if err = encoder.Encode(resp); err != nil {
|
||||
a.log.Debugln("Encode error:", err)
|
||||
|
@ -273,7 +273,7 @@ func (c *Core) PublicKey() ed25519.PublicKey {
|
||||
// Hack to get the admin stuff working, TODO something cleaner
|
||||
|
||||
type AddHandler interface {
|
||||
AddHandler(name string, args []string, handlerfunc AddHandlerFunc) error
|
||||
AddHandler(name, desc string, args []string, handlerfunc AddHandlerFunc) error
|
||||
}
|
||||
|
||||
type AddHandlerFunc func(json.RawMessage) (interface{}, error)
|
||||
@ -281,16 +281,28 @@ type AddHandlerFunc func(json.RawMessage) (interface{}, error)
|
||||
// SetAdmin must be called after Init and before Start.
|
||||
// It sets the admin handler for NodeInfo and the Debug admin functions.
|
||||
func (c *Core) SetAdmin(a AddHandler) error {
|
||||
if err := a.AddHandler("getNodeInfo", []string{"key"}, c.proto.nodeinfo.nodeInfoAdminHandler); err != nil {
|
||||
if err := a.AddHandler(
|
||||
"getNodeInfo", "Request nodeinfo from a remote node by its public key", []string{"key"},
|
||||
c.proto.nodeinfo.nodeInfoAdminHandler,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := a.AddHandler("debug_remoteGetSelf", []string{"key"}, c.proto.getSelfHandler); err != nil {
|
||||
if err := a.AddHandler(
|
||||
"debug_remoteGetSelf", "Debug use only", []string{"key"},
|
||||
c.proto.getSelfHandler,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := a.AddHandler("debug_remoteGetPeers", []string{"key"}, c.proto.getPeersHandler); err != nil {
|
||||
if err := a.AddHandler(
|
||||
"debug_remoteGetPeers", "Debug use only", []string{"key"},
|
||||
c.proto.getPeersHandler,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := a.AddHandler("debug_remoteGetDHT", []string{"key"}, c.proto.getDHTHandler); err != nil {
|
||||
if err := a.AddHandler(
|
||||
"debug_remoteGetDHT", "Debug use only", []string{"key"},
|
||||
c.proto.getDHTHandler,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -20,15 +20,18 @@ func (m *Multicast) getMulticastInterfacesHandler(req *GetMulticastInterfacesReq
|
||||
}
|
||||
|
||||
func (m *Multicast) SetupAdminHandlers(a *admin.AdminSocket) {
|
||||
_ = a.AddHandler("getMulticastInterfaces", []string{}, func(in json.RawMessage) (interface{}, error) {
|
||||
req := &GetMulticastInterfacesRequest{}
|
||||
res := &GetMulticastInterfacesResponse{}
|
||||
if err := json.Unmarshal(in, &req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := m.getMulticastInterfacesHandler(req, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
})
|
||||
_ = a.AddHandler(
|
||||
"getMulticastInterfaces", "Show which interfaces multicast is enabled on", []string{},
|
||||
func(in json.RawMessage) (interface{}, error) {
|
||||
req := &GetMulticastInterfacesRequest{}
|
||||
res := &GetMulticastInterfacesResponse{}
|
||||
if err := json.Unmarshal(in, &req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := m.getMulticastInterfacesHandler(req, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -7,31 +7,39 @@ import (
|
||||
)
|
||||
|
||||
type GetTUNRequest struct{}
|
||||
type GetTUNResponse map[string]TUNEntry
|
||||
type GetTUNResponse struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Name string `json:"name,omitempty"`
|
||||
MTU uint64 `json:"mtu,omitempty"`
|
||||
}
|
||||
|
||||
type TUNEntry struct {
|
||||
MTU uint64 `json:"mtu"`
|
||||
}
|
||||
|
||||
func (t *TunAdapter) getTUNHandler(req *GetTUNRequest, res *GetTUNResponse) error {
|
||||
*res = GetTUNResponse{
|
||||
t.Name(): TUNEntry{
|
||||
MTU: t.MTU(),
|
||||
},
|
||||
res.Enabled = t.isEnabled
|
||||
if !t.isEnabled {
|
||||
return nil
|
||||
}
|
||||
res.Name = t.Name()
|
||||
res.MTU = t.MTU()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TunAdapter) SetupAdminHandlers(a *admin.AdminSocket) {
|
||||
_ = a.AddHandler("getTunTap", []string{}, func(in json.RawMessage) (interface{}, error) {
|
||||
req := &GetTUNRequest{}
|
||||
res := &GetTUNResponse{}
|
||||
if err := json.Unmarshal(in, &req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := t.getTUNHandler(req, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
})
|
||||
_ = a.AddHandler(
|
||||
"getTun", "Show information about the node's TUN interface", []string{},
|
||||
func(in json.RawMessage) (interface{}, error) {
|
||||
req := &GetTUNRequest{}
|
||||
res := &GetTUNResponse{}
|
||||
if err := json.Unmarshal(in, &req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := t.getTUNHandler(req, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
},
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user