diff --git a/cmd/yggdrasilctl/cmd_line_env.go b/cmd/yggdrasilctl/cmd_line_env.go index c1acacd..9fcabad 100644 --- a/cmd/yggdrasilctl/cmd_line_env.go +++ b/cmd/yggdrasilctl/cmd_line_env.go @@ -14,9 +14,9 @@ import ( ) type CmdLineEnv struct { - args []string - endpoint, server string - injson, verbose, ver bool + args []string + endpoint, server string + injson, ver bool } func newCmdLineEnv() CmdLineEnv { @@ -46,7 +46,6 @@ func (cmdLineEnv *CmdLineEnv) parseFlagsAndArgs() { server := flag.String("endpoint", cmdLineEnv.endpoint, "Admin socket endpoint") injson := flag.Bool("json", false, "Output in JSON format (as opposed to pretty-print)") - verbose := flag.Bool("v", false, "Verbose output (includes public keys)") ver := flag.Bool("version", false, "Prints the version of this build") flag.Parse() @@ -54,7 +53,6 @@ func (cmdLineEnv *CmdLineEnv) parseFlagsAndArgs() { cmdLineEnv.args = flag.Args() cmdLineEnv.server = *server cmdLineEnv.injson = *injson - cmdLineEnv.verbose = *verbose cmdLineEnv.ver = *ver } diff --git a/cmd/yggdrasilctl/main.go b/cmd/yggdrasilctl/main.go index 180bea0..5e8bee2 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/yggdrasilctl/main.go @@ -10,15 +10,15 @@ import ( "net" "net/url" "os" - "sort" - "strconv" "strings" + "time" + "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/version" ) -type admin_info map[string]interface{} - func main() { // makes sure we can use defer and still return an error code to the OS os.Exit(run()) @@ -54,103 +54,13 @@ func run() int { cmdLineEnv.setEndpoint(logger) - conn := connect(cmdLineEnv.endpoint, logger) - logger.Println("Connected") - defer conn.Close() - - decoder := json.NewDecoder(conn) - encoder := json.NewEncoder(conn) - send := make(admin_info) - recv := make(admin_info) - - for c, a := range cmdLineEnv.args { - if c == 0 { - if strings.HasPrefix(a, "-") { - logger.Printf("Ignoring flag %s as it should be specified before other parameters\n", a) - continue - } - logger.Printf("Sending request: %v\n", a) - send["request"] = a - continue - } - tokens := strings.Split(a, "=") - if len(tokens) == 1 { - send[tokens[0]] = true - } else if len(tokens) > 2 { - send[tokens[0]] = strings.Join(tokens[1:], "=") - } else if len(tokens) == 2 { - if i, err := strconv.Atoi(tokens[1]); err == nil { - logger.Printf("Sending parameter %s: %d\n", tokens[0], i) - send[tokens[0]] = i - } else { - switch strings.ToLower(tokens[1]) { - case "true": - send[tokens[0]] = true - case "false": - send[tokens[0]] = false - default: - send[tokens[0]] = tokens[1] - } - logger.Printf("Sending parameter %s: %v\n", tokens[0], send[tokens[0]]) - } - } - } - - if err := encoder.Encode(&send); err != nil { - panic(err) - } - - logger.Printf("Request sent") - - if err := decoder.Decode(&recv); err == nil { - logger.Printf("Response received") - if recv["status"] == "error" { - if err, ok := recv["error"]; ok { - fmt.Println("Admin socket returned an error:", err) - } else { - fmt.Println("Admin socket returned an error but didn't specify any error text") - } - return 1 - } - if _, ok := recv["request"]; !ok { - fmt.Println("Missing request in response (malformed response?)") - return 1 - } - if _, ok := recv["response"]; !ok { - fmt.Println("Missing response body (malformed response?)") - return 1 - } - res := recv["response"].(map[string]interface{}) - - if cmdLineEnv.injson { - if json, err := json.MarshalIndent(res, "", " "); err == nil { - fmt.Println(string(json)) - } - return 0 - } - - handleAll(recv, cmdLineEnv.verbose) - } else { - logger.Println("Error receiving response:", err) - } - - if v, ok := recv["status"]; ok && v != "success" { - return 1 - } - - return 0 -} - -func connect(endpoint string, logger *log.Logger) net.Conn { var conn net.Conn - - u, err := url.Parse(endpoint) - + u, err := url.Parse(cmdLineEnv.endpoint) if err == nil { switch strings.ToLower(u.Scheme) { case "unix": - logger.Println("Connecting to UNIX socket", endpoint[7:]) - conn, err = net.Dial("unix", endpoint[7:]) + logger.Println("Connecting to UNIX socket", cmdLineEnv.endpoint[7:]) + conn, err = net.Dial("unix", cmdLineEnv.endpoint[7:]) case "tcp": logger.Println("Connecting to TCP socket", u.Host) conn, err = net.Dial("tcp", u.Host) @@ -160,298 +70,174 @@ func connect(endpoint string, logger *log.Logger) net.Conn { } } else { logger.Println("Connecting to TCP socket", u.Host) - conn, err = net.Dial("tcp", endpoint) + conn, err = net.Dial("tcp", cmdLineEnv.endpoint) } - if err != nil { panic(err) } - return conn -} + logger.Println("Connected") + defer conn.Close() -func handleAll(recv map[string]interface{}, verbose bool) { - req := recv["request"].(map[string]interface{}) - res := recv["response"].(map[string]interface{}) + decoder := json.NewDecoder(conn) + encoder := json.NewEncoder(conn) + send := &admin.AdminSocketRequest{} + recv := &admin.AdminSocketResponse{} - switch strings.ToLower(req["request"].(string)) { - case "dot": - handleDot(res) - case "list", "getpeers", "getswitchpeers", "getdht", "getsessions", "dhtping": - handleVariousInfo(res, verbose) - case "gettuntap", "settuntap": - handleGetAndSetTunTap(res) - case "getself": - handleGetSelf(res, verbose) - case "getswitchqueues": - handleGetSwitchQueues(res) - case "addpeer", "removepeer", "addallowedencryptionpublickey", "removeallowedencryptionpublickey", "addsourcesubnet", "addroute", "removesourcesubnet", "removeroute": - handleAddsAndRemoves(res) - case "getallowedencryptionpublickeys": - handleGetAllowedEncryptionPublicKeys(res) - case "getmulticastinterfaces": - handleGetMulticastInterfaces(res) - case "getsourcesubnets": - handleGetSourceSubnets(res) - case "getroutes": - handleGetRoutes(res) - case "settunnelrouting": - fallthrough - case "gettunnelrouting": - handleGetTunnelRouting(res) - default: - if json, err := json.MarshalIndent(recv["response"], "", " "); err == nil { + for c, a := range cmdLineEnv.args { + if c == 0 { + if strings.HasPrefix(a, "-") { + logger.Printf("Ignoring flag %s as it should be specified before other parameters\n", a) + continue + } + logger.Printf("Sending request: %v\n", a) + send.Name = a + continue + } + tokens := strings.SplitN(a, "=", 1) + switch { + case len(tokens) == 1: + panic("incomplete argument supplied") + default: + send.Arguments[tokens[0]] = tokens[1] + } + } + + if err := encoder.Encode(&send); err != nil { + panic(err) + } + logger.Printf("Request sent") + if err := decoder.Decode(&recv); err != nil { + panic(err) + } + if recv.Status == "error" { + if err := recv.Error; err != "" { + fmt.Println("Admin socket returned an error:", err) + } else { + fmt.Println("Admin socket returned an error but didn't specify any error text") + } + return 1 + } + if cmdLineEnv.injson { + if json, err := json.MarshalIndent(recv.Response, "", " "); err == nil { fmt.Println(string(json)) } + return 0 } -} - -func handleDot(res map[string]interface{}) { - fmt.Println(res["dot"]) -} - -func handleVariousInfo(res map[string]interface{}, verbose bool) { - maxWidths := make(map[string]int) - var keyOrder []string - keysOrdered := false - - for _, tlv := range res { - for slk, slv := range tlv.(map[string]interface{}) { - if !keysOrdered { - for k := range slv.(map[string]interface{}) { - if !verbose { - if k == "box_pub_key" || k == "box_sig_key" || k == "nodeinfo" || k == "was_mtu_fixed" { - continue - } - } - keyOrder = append(keyOrder, fmt.Sprint(k)) - } - sort.Strings(keyOrder) - keysOrdered = true - } - for k, v := range slv.(map[string]interface{}) { - if len(fmt.Sprint(slk)) > maxWidths["key"] { - maxWidths["key"] = len(fmt.Sprint(slk)) - } - if len(fmt.Sprint(v)) > maxWidths[k] { - maxWidths[k] = len(fmt.Sprint(v)) - if maxWidths[k] < len(k) { - maxWidths[k] = len(k) - } - } - } - } - - if len(keyOrder) > 0 { - fmt.Printf("%-"+fmt.Sprint(maxWidths["key"])+"s ", "") - for _, v := range keyOrder { - fmt.Printf("%-"+fmt.Sprint(maxWidths[v])+"s ", v) - } - fmt.Println() - } - - for slk, slv := range tlv.(map[string]interface{}) { - fmt.Printf("%-"+fmt.Sprint(maxWidths["key"])+"s ", slk) - for _, k := range keyOrder { - preformatted := slv.(map[string]interface{})[k] - var formatted string - switch k { - case "bytes_sent", "bytes_recvd": - formatted = fmt.Sprintf("%d", uint(preformatted.(float64))) - case "uptime", "last_seen": - seconds := uint(preformatted.(float64)) % 60 - minutes := uint(preformatted.(float64)/60) % 60 - hours := uint(preformatted.(float64) / 60 / 60) - formatted = fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds) - default: - formatted = fmt.Sprint(preformatted) - } - fmt.Printf("%-"+fmt.Sprint(maxWidths[k])+"s ", formatted) - } - fmt.Println() - } - } -} - -func handleGetAndSetTunTap(res map[string]interface{}) { - for k, v := range res { - fmt.Println("Interface name:", k) - if mtu, ok := v.(map[string]interface{})["mtu"].(float64); ok { - fmt.Println("Interface MTU:", mtu) - } - if tap_mode, ok := v.(map[string]interface{})["tap_mode"].(bool); ok { - fmt.Println("TAP mode:", tap_mode) - } - } -} - -func handleGetSelf(res map[string]interface{}, verbose bool) { - for k, v := range res["self"].(map[string]interface{}) { - if buildname, ok := v.(map[string]interface{})["build_name"].(string); ok && buildname != "unknown" { - fmt.Println("Build name:", buildname) - } - if buildversion, ok := v.(map[string]interface{})["build_version"].(string); ok && buildversion != "unknown" { - fmt.Println("Build version:", buildversion) - } - fmt.Println("IPv6 address:", k) - if subnet, ok := v.(map[string]interface{})["subnet"].(string); ok { - fmt.Println("IPv6 subnet:", subnet) - } - if boxSigKey, ok := v.(map[string]interface{})["key"].(string); ok { - fmt.Println("Public key:", boxSigKey) - } - if coords, ok := v.(map[string]interface{})["coords"].([]interface{}); ok { - fmt.Println("Coords:", coords) - } - if verbose { - if nodeID, ok := v.(map[string]interface{})["node_id"].(string); ok { - fmt.Println("Node ID:", nodeID) - } - if boxPubKey, ok := v.(map[string]interface{})["box_pub_key"].(string); ok { - fmt.Println("Public encryption key:", boxPubKey) - } - if boxSigKey, ok := v.(map[string]interface{})["box_sig_key"].(string); ok { - fmt.Println("Public signing key:", boxSigKey) - } - } - } -} - -func handleGetSwitchQueues(res map[string]interface{}) { - maximumqueuesize := float64(4194304) - portqueues := make(map[float64]float64) - portqueuesize := make(map[float64]float64) - portqueuepackets := make(map[float64]float64) - v := res["switchqueues"].(map[string]interface{}) - if queuecount, ok := v["queues_count"].(float64); ok { - fmt.Printf("Active queue count: %d queues\n", uint(queuecount)) - } - if queuesize, ok := v["queues_size"].(float64); ok { - fmt.Printf("Active queue size: %d bytes\n", uint(queuesize)) - } - if highestqueuecount, ok := v["highest_queues_count"].(float64); ok { - fmt.Printf("Highest queue count: %d queues\n", uint(highestqueuecount)) - } - if highestqueuesize, ok := v["highest_queues_size"].(float64); ok { - fmt.Printf("Highest queue size: %d bytes\n", uint(highestqueuesize)) - } - if m, ok := v["maximum_queues_size"].(float64); ok { - maximumqueuesize = m - fmt.Printf("Maximum queue size: %d bytes\n", uint(maximumqueuesize)) - } - if queues, ok := v["queues"].([]interface{}); ok { - if len(queues) != 0 { - fmt.Println("Active queues:") - for _, v := range queues { - queueport := v.(map[string]interface{})["queue_port"].(float64) - queuesize := v.(map[string]interface{})["queue_size"].(float64) - queuepackets := v.(map[string]interface{})["queue_packets"].(float64) - queueid := v.(map[string]interface{})["queue_id"].(string) - portqueues[queueport]++ - portqueuesize[queueport] += queuesize - portqueuepackets[queueport] += queuepackets - queuesizepercent := (100 / maximumqueuesize) * queuesize - fmt.Printf("- Switch port %d, Stream ID: %v, size: %d bytes (%d%% full), %d packets\n", - uint(queueport), []byte(queueid), uint(queuesize), - uint(queuesizepercent), uint(queuepackets)) - } - } - } - if len(portqueuesize) > 0 && len(portqueuepackets) > 0 { - fmt.Println("Aggregated statistics by switchport:") - for k, v := range portqueuesize { - queuesizepercent := (100 / (portqueues[k] * maximumqueuesize)) * v - fmt.Printf("- Switch port %d, size: %d bytes (%d%% full), %d packets\n", - uint(k), uint(v), uint(queuesizepercent), uint(portqueuepackets[k])) - } - } -} - -func handleAddsAndRemoves(res map[string]interface{}) { - if _, ok := res["added"]; ok { - for _, v := range res["added"].([]interface{}) { - fmt.Println("Added:", fmt.Sprint(v)) - } - } - if _, ok := res["not_added"]; ok { - for _, v := range res["not_added"].([]interface{}) { - fmt.Println("Not added:", fmt.Sprint(v)) - } - } - if _, ok := res["removed"]; ok { - for _, v := range res["removed"].([]interface{}) { - fmt.Println("Removed:", fmt.Sprint(v)) - } - } - if _, ok := res["not_removed"]; ok { - for _, v := range res["not_removed"].([]interface{}) { - fmt.Println("Not removed:", fmt.Sprint(v)) - } - } -} - -func handleGetAllowedEncryptionPublicKeys(res map[string]interface{}) { - if _, ok := res["allowed_box_pubs"]; !ok { - fmt.Println("All connections are allowed") - } else if res["allowed_box_pubs"] == nil { - fmt.Println("All connections are allowed") - } else { - fmt.Println("Connections are allowed only from the following public box keys:") - for _, v := range res["allowed_box_pubs"].([]interface{}) { - fmt.Println("-", v) - } - } -} - -func handleGetMulticastInterfaces(res map[string]interface{}) { - if _, ok := res["multicast_interfaces"]; !ok { - fmt.Println("No multicast interfaces found") - } else if res["multicast_interfaces"] == nil { - fmt.Println("No multicast interfaces found") - } else { - fmt.Println("Multicast peer discovery is active on:") - for _, v := range res["multicast_interfaces"].([]interface{}) { - fmt.Println("-", v) - } - } -} - -func handleGetSourceSubnets(res map[string]interface{}) { - if _, ok := res["source_subnets"]; !ok { - fmt.Println("No source subnets found") - } else if res["source_subnets"] == nil { - fmt.Println("No source subnets found") - } else { - fmt.Println("Source subnets:") - for _, v := range res["source_subnets"].([]interface{}) { - fmt.Println("-", v) - } - } -} - -func handleGetRoutes(res map[string]interface{}) { - if routes, ok := res["routes"].(map[string]interface{}); !ok { - fmt.Println("No routes found") - } else { - if res["routes"] == nil || len(routes) == 0 { - fmt.Println("No routes found") - } else { - fmt.Println("Routes:") - for k, v := range routes { - if pv, ok := v.(string); ok { - fmt.Println("-", k, " via ", pv) - } - } - } - } -} - -func handleGetTunnelRouting(res map[string]interface{}) { - if enabled, ok := res["enabled"].(bool); !ok { - fmt.Println("Tunnel routing is disabled") - } else if !enabled { - fmt.Println("Tunnel routing is disabled") - } else { - fmt.Println("Tunnel routing is enabled") - } + + table := tablewriter.NewWriter(os.Stdout) + table.SetAlignment(tablewriter.ALIGN_LEFT) + table.SetAutoFormatHeaders(false) + table.SetCenterSeparator("") + table.SetColumnSeparator("") + table.SetRowSeparator("") + table.SetHeaderLine(false) + table.SetBorder(false) + table.SetTablePadding("\t") // pad with tabs + table.SetNoWhiteSpace(true) + + switch strings.ToLower(recv.Request.Name) { + case "list": + var resp admin.ListResponse + if err := json.Unmarshal(recv.Response, &resp); err != nil { + panic(err) + } + table.SetHeader([]string{"Command", "Arguments"}) + for _, entry := range resp.List { + table.Append([]string{entry.Command, strings.Join(entry.Fields, ", ")}) + } + table.Render() + + case "getself": + var resp admin.GetSelfResponse + if err := json.Unmarshal(recv.Response, &resp); err != nil { + panic(err) + } + table.Append([]string{"Build name:", resp.BuildName}) + table.Append([]string{"Build version:", resp.BuildVersion}) + table.Append([]string{"IPv6 address:", resp.IPAddress}) + table.Append([]string{"IPv6 subnet:", resp.Subnet}) + table.Append([]string{"Coordinates:", fmt.Sprintf("%v", resp.Coords)}) + table.Append([]string{"Public key:", resp.PublicKey}) + table.Render() + + case "getpeers": + var resp admin.GetPeersResponse + if err := json.Unmarshal(recv.Response, &resp); err != nil { + panic(err) + } + table.SetHeader([]string{"Port", "Public Key", "IP Address", "Peering URI", "Uptime", "RX", "TX"}) + for _, peer := range resp.Peers { + table.Append([]string{ + fmt.Sprintf("%d", peer.Port), + peer.PublicKey, + peer.IPAddress, + peer.Remote, + (time.Duration(peer.Uptime) * time.Second).String(), + peer.RXBytes.String(), + peer.TXBytes.String(), + }) + } + table.Render() + + case "getdht": + var resp admin.GetDHTResponse + if err := json.Unmarshal(recv.Response, &resp); err != nil { + panic(err) + } + table.SetHeader([]string{"Public Key", "IP Address", "Port", "Rest"}) + for _, dht := range resp.DHT { + table.Append([]string{ + dht.PublicKey, + dht.IPAddress, + fmt.Sprintf("%d", dht.Port), + fmt.Sprintf("%d", dht.Rest), + }) + } + table.Render() + + case "getpaths": + var resp admin.GetPathsResponse + if err := json.Unmarshal(recv.Response, &resp); err != nil { + panic(err) + } + table.SetHeader([]string{"Public Key", "IP Address", "Path"}) + for _, p := range resp.Paths { + table.Append([]string{ + p.PublicKey, + p.IPAddress, + fmt.Sprintf("%v", p.Path), + }) + } + table.Render() + + case "getsessions": + var resp admin.GetSessionsResponse + if err := json.Unmarshal(recv.Response, &resp); err != nil { + panic(err) + } + table.SetHeader([]string{"Public Key", "IP Address"}) + for _, p := range resp.Sessions { + table.Append([]string{ + p.PublicKey, + p.IPAddress, + }) + } + table.Render() + + case "getnodeinfo": + var resp core.GetNodeInfoResponse + if err := json.Unmarshal(recv.Response, &resp); err != nil { + panic(err) + } + for _, v := range resp { + fmt.Println(string(v)) + break + } + + default: + panic("unknown response type: " + recv.Request.Name) + } + + return 0 } diff --git a/go.mod b/go.mod index 11170a1..c790b19 100644 --- a/go.mod +++ b/go.mod @@ -13,23 +13,27 @@ require ( github.com/mitchellh/mapstructure v1.4.1 github.com/vishvananda/netlink v1.1.0 golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 - golang.org/x/net v0.0.0-20211101193420-4a448f8816b3 - golang.org/x/sys v0.0.0-20211102192858-4dd72447c267 + golang.org/x/net v0.0.0-20220722155237-a158d28d115b + golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a golang.zx2c4.com/wireguard/windows v0.4.12 ) +require ( + github.com/mattn/go-colorable v0.1.8 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect + golang.org/x/tools v0.1.12 // indirect +) + require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/fatih/color v1.12.0 // indirect - github.com/mattn/go-colorable v0.1.8 // indirect github.com/mattn/go-isatty v0.0.13 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect - github.com/rivo/uniseg v0.2.0 // indirect + github.com/olekukonko/tablewriter v0.0.5 + github.com/onsi/gomega v1.20.2 github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect - golang.org/x/mod v0.4.2 // indirect - golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect ) diff --git a/go.sum b/go.sum index 0fc5cad..5d22d67 100644 --- a/go.sum +++ b/go.sum @@ -8,15 +8,40 @@ github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1o github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/cheggaaa/pb/v3 v3.0.8 h1:bC8oemdChbke2FHIIGy9mn4DPJ2caZYQnfbRqwmdCoA= github.com/cheggaaa/pb/v3 v3.0.8/go.mod h1:UICbiLec/XO6Hw6k+BHEtHeQFzzBH4i2/qk/ow1EJTA= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/gologme/log v1.2.0 h1:Ya5Ip/KD6FX7uH0S31QO87nCCSucKtF44TLbTtO7V4c= github.com/gologme/log v1.2.0/go.mod h1:gq31gQ8wEHkR+WekdWsqDuf8pXTUZA9BnnzTuPz1Y9U= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hjson/hjson-go v3.1.0+incompatible h1:DY/9yE8ey8Zv22bY+mHV1uk2yRy0h8tKhZ77hEdi0Aw= github.com/hjson/hjson-go v3.1.0+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/kardianos/minwinsvc v1.0.0 h1:+JfAi8IBJna0jY2dJGZqi7o15z13JelFIklJCAENALA= github.com/kardianos/minwinsvc v1.0.0/go.mod h1:Bgd0oc+D0Qo3bBytmNtyRKVlp85dAloLKhfxanPFFRc= github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= @@ -26,23 +51,48 @@ github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= +github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= +github.com/onsi/gomega v1.20.2 h1:8uQq0zMgLEfa0vRrrBgaJF2gyW9Da9BmfGV+OyUzfkY= +github.com/onsi/gomega v1.20.2/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -50,59 +100,105 @@ golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9t golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20220112015953-858099ff7816 h1:jhDgkcu3yQ4tasBZ+1YwDmK7eFmuVf1w1k+NGGGxfmE= -golang.org/x/mobile v0.0.0-20220112015953-858099ff7816/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 h1:3vUV5x5+3LfQbgk7paCM6INOaJG9xXQbn79xoNkwfIk= golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210927181540-4e4d966f7476/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211011170408-caeb26a5c8c0/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211101193420-4a448f8816b3 h1:VrJZAjbekhoRn7n5FBujY31gboH+iB3pdLxn3gE9FjU= -golang.org/x/net v0.0.0-20211101193420-4a448f8816b3/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211102192858-4dd72447c267 h1:7zYaz3tjChtpayGDzu6H0hDAUM5zIGA2XW7kRNgQ0jc= -golang.org/x/sys v0.0.0-20211102192858-4dd72447c267/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b h1:NXqSWXSRUSCaFuvitrWtU169I3876zRTalMRbfd6LL0= golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= -golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098 h1:YuekqPskqwCCPM79F1X5Dhv4ezTCj+Ki1oNwiafxkA0= golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wireguard v0.0.0-20211012062646-82d2aa87aa62/go.mod h1:id8Oh3eCCmpj9uVGWVjsUAl6UPX5ysMLzu6QxJU2UOU= golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a h1:tTbyylK9/D3u/wEP26Vx7L700UpY48nhioJWZM1vhZw= golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a/go.mod h1:id8Oh3eCCmpj9uVGWVjsUAl6UPX5ysMLzu6QxJU2UOU= golang.zx2c4.com/wireguard/windows v0.4.12 h1:CUmbdWKVNzTSsVb4yUAiEwL3KsabdJkEPdDjCHxBlhA= golang.zx2c4.com/wireguard/windows v0.4.12/go.mod h1:PW4y+d9oY83XU9rRwRwrJDwEMuhVjMxu2gfD1cfzS7w= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/src/admin/admin.go b/src/admin/admin.go index c7c0f14..9a00d8a 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -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) + } +} diff --git a/src/admin/getdht.go b/src/admin/getdht.go index 5dc9554..bfb2181 100644 --- a/src/admin/getdht.go +++ b/src/admin/getdht.go @@ -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 } diff --git a/src/admin/getpaths.go b/src/admin/getpaths.go index c8e97d0..fbd5248 100644 --- a/src/admin/getpaths.go +++ b/src/admin/getpaths.go @@ -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 } diff --git a/src/admin/getpeers.go b/src/admin/getpeers.go index ecb2872..61d0937 100644 --- a/src/admin/getpeers.go +++ b/src/admin/getpeers.go @@ -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 } diff --git a/src/admin/getself.go b/src/admin/getself.go index 7effcc4..f42dc75 100644 --- a/src/admin/getself.go +++ b/src/admin/getself.go @@ -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 } diff --git a/src/admin/getsessions.go b/src/admin/getsessions.go index 3a0c19b..58ce378 100644 --- a/src/admin/getsessions.go +++ b/src/admin/getsessions.go @@ -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 } diff --git a/src/core/nodeinfo.go b/src/core/nodeinfo.go index 4ca21d7..a6132ec 100644 --- a/src/core/nodeinfo.go +++ b/src/core/nodeinfo.go @@ -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 } }