Admin socket and yggdrasilctl refactoring (#939)

This commit is contained in:
Neil Alexander 2022-09-03 10:50:43 +01:00 committed by GitHub
parent 4f2abece81
commit c6fe81b5d2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 401 additions and 470 deletions

View file

@ -7,6 +7,7 @@ import (
"net"
"net/url"
"os"
"sort"
"strings"
"time"
@ -28,13 +29,17 @@ type AdminSocket struct {
done chan struct{}
}
type AdminSocketRequest struct {
Name string `json:"request"`
Arguments map[string]string `json:"arguments,omitempty"`
KeepAlive bool `json:"keepalive,omitempty"`
}
type AdminSocketResponse struct {
Status string `json:"status"`
Request struct {
Name string `json:"request"`
KeepAlive bool `json:"keepalive"`
} `json:"request"`
Response interface{} `json:"response"`
Status string `json:"status"`
Error string `json:"error,omitempty"`
Request AdminSocketRequest `json:"request"`
Response json.RawMessage `json:"response"`
}
type handler struct {
@ -43,11 +48,12 @@ type handler struct {
}
type ListResponse struct {
List map[string]ListEntry `json:"list"`
List []ListEntry `json:"list"`
}
type ListEntry struct {
Fields []string `json:"fields"`
Command string `json:"command"`
Fields []string `json:"fields,omitempty"`
}
// AddHandler is called for each admin function to add the handler and help documentation to the API.
@ -73,14 +79,16 @@ func (a *AdminSocket) Init(c *core.Core, nc *config.NodeConfig, log *log.Logger,
a.done = make(chan struct{})
close(a.done) // Start in a done / not-started state
_ = a.AddHandler("list", []string{}, func(_ json.RawMessage) (interface{}, error) {
res := &ListResponse{
List: map[string]ListEntry{},
}
res := &ListResponse{}
for name, handler := range a.handlers {
res.List[name] = ListEntry{
Fields: handler.args,
}
res.List = append(res.List, ListEntry{
Command: name,
Fields: handler.args,
})
}
sort.SliceStable(res.List, func(i, j int) bool {
return strings.Compare(res.List[i].Command, res.List[j].Command) < 0
})
return res, nil
})
return a.core.SetAdmin(a)
@ -277,22 +285,28 @@ func (a *AdminSocket) handleRequest(conn net.Conn) {
if err = json.Unmarshal(buf, &resp.Request); err == nil {
if resp.Request.Name == "" {
resp.Status = "error"
resp.Response = &ErrorResponse{
resp.Response, _ = json.Marshal(ErrorResponse{
Error: "No request specified",
}
})
} else if h, ok := a.handlers[strings.ToLower(resp.Request.Name)]; ok {
resp.Response, err = h.handler(buf)
res, err := h.handler(buf)
if err != nil {
resp.Status = "error"
resp.Response = &ErrorResponse{
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 = &ErrorResponse{
resp.Response, _ = json.Marshal(ErrorResponse{
Error: fmt.Sprintf("Unknown action '%s', try 'list' for help", resp.Request.Name),
}
})
}
}
if err = encoder.Encode(resp); err != nil {
@ -305,3 +319,16 @@ func (a *AdminSocket) handleRequest(conn net.Conn) {
}
}
}
type DataUnit uint64
func (d DataUnit) String() string {
switch {
case d > 1024*1024*1024:
return fmt.Sprintf("%2.fgb", float64(d)/1024/1024/1024)
case d > 1024*1024:
return fmt.Sprintf("%2.fmb", float64(d)/1024/1024)
default:
return fmt.Sprintf("%2.fkb", float64(d)/1024)
}
}

View file

@ -3,6 +3,8 @@ package admin
import (
"encoding/hex"
"net"
"sort"
"strings"
"github.com/yggdrasil-network/yggdrasil-go/src/address"
)
@ -10,25 +12,30 @@ import (
type GetDHTRequest struct{}
type GetDHTResponse struct {
DHT map[string]DHTEntry `json:"dht"`
DHT []DHTEntry `json:"dht"`
}
type DHTEntry struct {
IPAddress string `json:"address"`
PublicKey string `json:"key"`
Port uint64 `json:"port"`
Rest uint64 `json:"rest"`
}
func (a *AdminSocket) getDHTHandler(req *GetDHTRequest, res *GetDHTResponse) error {
res.DHT = map[string]DHTEntry{}
for _, d := range a.core.GetDHT() {
dht := a.core.GetDHT()
res.DHT = make([]DHTEntry, 0, len(dht))
for _, d := range dht {
addr := address.AddrForKey(d.Key)
so := net.IP(addr[:]).String()
res.DHT[so] = DHTEntry{
res.DHT = append(res.DHT, DHTEntry{
IPAddress: net.IP(addr[:]).String(),
PublicKey: hex.EncodeToString(d.Key[:]),
Port: d.Port,
Rest: d.Rest,
}
})
}
sort.SliceStable(res.DHT, func(i, j int) bool {
return strings.Compare(res.DHT[i].PublicKey, res.DHT[j].PublicKey) < 0
})
return nil
}

View file

@ -3,6 +3,8 @@ package admin
import (
"encoding/hex"
"net"
"sort"
"strings"
"github.com/yggdrasil-network/yggdrasil-go/src/address"
)
@ -11,23 +13,28 @@ type GetPathsRequest struct {
}
type GetPathsResponse struct {
Paths map[string]PathEntry `json:"paths"`
Paths []PathEntry `json:"paths"`
}
type PathEntry struct {
IPAddress string `json:"address"`
PublicKey string `json:"key"`
Path []uint64 `json:"path"`
}
func (a *AdminSocket) getPathsHandler(req *GetPathsRequest, res *GetPathsResponse) error {
res.Paths = map[string]PathEntry{}
for _, p := range a.core.GetPaths() {
paths := a.core.GetPaths()
res.Paths = make([]PathEntry, 0, len(paths))
for _, p := range paths {
addr := address.AddrForKey(p.Key)
so := net.IP(addr[:]).String()
res.Paths[so] = PathEntry{
res.Paths = append(res.Paths, PathEntry{
IPAddress: net.IP(addr[:]).String(),
PublicKey: hex.EncodeToString(p.Key),
Path: p.Path,
}
})
}
sort.SliceStable(res.Paths, func(i, j int) bool {
return strings.Compare(res.Paths[i].PublicKey, res.Paths[j].PublicKey) < 0
})
return nil
}

View file

@ -3,6 +3,7 @@ package admin
import (
"encoding/hex"
"net"
"sort"
"github.com/yggdrasil-network/yggdrasil-go/src/address"
)
@ -11,33 +12,38 @@ type GetPeersRequest struct {
}
type GetPeersResponse struct {
Peers map[string]PeerEntry `json:"peers"`
Peers []PeerEntry `json:"peers"`
}
type PeerEntry struct {
IPAddress string `json:"address"`
PublicKey string `json:"key"`
Port uint64 `json:"port"`
Coords []uint64 `json:"coords"`
Remote string `json:"remote"`
RXBytes uint64 `json:"bytes_recvd"`
TXBytes uint64 `json:"bytes_sent"`
RXBytes DataUnit `json:"bytes_recvd"`
TXBytes DataUnit `json:"bytes_sent"`
Uptime float64 `json:"uptime"`
}
func (a *AdminSocket) getPeersHandler(req *GetPeersRequest, res *GetPeersResponse) error {
res.Peers = map[string]PeerEntry{}
for _, p := range a.core.GetPeers() {
peers := a.core.GetPeers()
res.Peers = make([]PeerEntry, 0, len(peers))
for _, p := range peers {
addr := address.AddrForKey(p.Key)
so := net.IP(addr[:]).String()
res.Peers[so] = PeerEntry{
res.Peers = append(res.Peers, PeerEntry{
IPAddress: net.IP(addr[:]).String(),
PublicKey: hex.EncodeToString(p.Key),
Port: p.Port,
Coords: p.Coords,
Remote: p.Remote,
RXBytes: p.RXBytes,
TXBytes: p.TXBytes,
RXBytes: DataUnit(p.RXBytes),
TXBytes: DataUnit(p.TXBytes),
Uptime: p.Uptime.Seconds(),
}
})
}
sort.Slice(res.Peers, func(i, j int) bool {
return res.Peers[i].Port < res.Peers[j].Port
})
return nil
}

View file

@ -9,28 +9,22 @@ import (
type GetSelfRequest struct{}
type GetSelfResponse struct {
Self map[string]SelfEntry `json:"self"`
}
type SelfEntry struct {
BuildName string `json:"build_name"`
BuildVersion string `json:"build_version"`
PublicKey string `json:"key"`
IPAddress string `json:"address"`
Coords []uint64 `json:"coords"`
Subnet string `json:"subnet"`
}
func (a *AdminSocket) getSelfHandler(req *GetSelfRequest, res *GetSelfResponse) error {
res.Self = make(map[string]SelfEntry)
self := a.core.GetSelf()
addr := a.core.Address().String()
snet := a.core.Subnet()
res.Self[addr] = SelfEntry{
BuildName: version.BuildName(),
BuildVersion: version.BuildVersion(),
PublicKey: hex.EncodeToString(self.Key[:]),
Subnet: snet.String(),
Coords: self.Coords,
}
res.BuildName = version.BuildName()
res.BuildVersion = version.BuildVersion()
res.PublicKey = hex.EncodeToString(self.Key[:])
res.IPAddress = a.core.Address().String()
res.Subnet = snet.String()
res.Coords = self.Coords
return nil
}

View file

@ -3,6 +3,8 @@ package admin
import (
"encoding/hex"
"net"
"sort"
"strings"
"github.com/yggdrasil-network/yggdrasil-go/src/address"
)
@ -10,21 +12,26 @@ import (
type GetSessionsRequest struct{}
type GetSessionsResponse struct {
Sessions map[string]SessionEntry `json:"sessions"`
Sessions []SessionEntry `json:"sessions"`
}
type SessionEntry struct {
IPAddress string `json:"address"`
PublicKey string `json:"key"`
}
func (a *AdminSocket) getSessionsHandler(req *GetSessionsRequest, res *GetSessionsResponse) error {
res.Sessions = map[string]SessionEntry{}
for _, s := range a.core.GetSessions() {
sessions := a.core.GetSessions()
res.Sessions = make([]SessionEntry, 0, len(sessions))
for _, s := range sessions {
addr := address.AddrForKey(s.Key)
so := net.IP(addr[:]).String()
res.Sessions[so] = SessionEntry{
res.Sessions = append(res.Sessions, SessionEntry{
IPAddress: net.IP(addr[:]).String(),
PublicKey: hex.EncodeToString(s.Key[:]),
}
})
}
sort.SliceStable(res.Sessions, func(i, j int) bool {
return strings.Compare(res.Sessions[i].PublicKey, res.Sessions[j].PublicKey) < 0
})
return nil
}

View file

@ -4,7 +4,6 @@ import (
"encoding/hex"
"encoding/json"
"errors"
"net"
"runtime"
"strings"
"time"
@ -13,7 +12,7 @@ import (
"github.com/Arceliar/phony"
//"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
"github.com/yggdrasil-network/yggdrasil-go/src/address"
"github.com/yggdrasil-network/yggdrasil-go/src/version"
)
@ -154,7 +153,7 @@ func (m *nodeinfo) _sendRes(key keyArray) {
type GetNodeInfoRequest struct {
Key string `json:"key"`
}
type GetNodeInfoResponse map[string]interface{}
type GetNodeInfoResponse map[string]json.RawMessage
func (m *nodeinfo) nodeInfoAdminHandler(in json.RawMessage) (interface{}, error) {
var req GetNodeInfoRequest
@ -182,8 +181,8 @@ func (m *nodeinfo) nodeInfoAdminHandler(in json.RawMessage) (interface{}, error)
if err := msg.UnmarshalJSON(info); err != nil {
return nil, err
}
ip := net.IP(address.AddrForKey(kbs)[:])
res := GetNodeInfoResponse{ip.String(): msg}
key := hex.EncodeToString(kbs[:])
res := GetNodeInfoResponse{key: msg}
return res, nil
}
}