Documentation
¶
Overview ¶
Package clisd cmd/skywire-cli/commands/sd/root.go c4-vis-cli
Package clisd cmd/skywire-cli/commands/sd/transport_counts.go c4-vis-cli
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var RootCmd = &cobra.Command{ Use: "sd", Short: "Service discovery network statistics", Long: fmt.Sprintf(`Display combined service discovery and transport statistics. Combines data from: - Service Discovery: %v/api/services (proxy / vpn / visor entries) - Transport Discovery: %v/all-transports (transport counts by type) - Uptime Tracker: online/offline status (color-coded rows) Shows public keys with their advertised services and transport counts by type (stcpr/squicr/sudph/stcp/webrtc/swsr/swtr/dmsg). Filter with --country, --version, --min (minimum transport count); --noton keeps offline/not-in-UT visors. --json emits the combined rows as machine-readable output. Use --testenv or SKYWIRETEST=1 to use test deployment services.`, getDeployment().ServiceDiscovery, getDeployment().TransportDiscovery), Run: func(cmd *cobra.Command, _ []string) { if testEnv && !isTestEnv() { if !cmd.Flags().Changed("sdurl") { sdURL = deployment.Test.ServiceDiscovery } if !cmd.Flags().Changed("tpdurl") { tpdURL = deployment.Test.TransportDiscovery } if !cmd.Flags().Changed("uturl") { utURL = deployment.Test.TransportDiscovery } if !cmd.Flags().Changed("cds") { cacheDirSD = cacheDirPath(deployment.Test.ServiceDiscovery) } if !cmd.Flags().Changed("cdt") { cacheDirTPD = cacheDirPath(deployment.Test.TransportDiscovery) } if !cmd.Flags().Changed("cdu") { cacheDirUT = cacheDirPath(deployment.Test.TransportDiscovery) } } sdProxyURL := sdURL + "/api/services?type=" + servicedisc.ServiceTypeProxy sdVpnURL := sdURL + "/api/services?type=" + servicedisc.ServiceTypeVPN sdVisorURL := sdURL + "/api/services?type=" + servicedisc.ServiceTypeVisor tpdFullURL := tpdURL + "/all-transports" utFullURL := clirpc.IntegratedUptimeURLDays(utURL, 1) proxyData := clirpc.FetchCachedServiceURL(cmd.Flags(), cacheFile(cacheDirSD, sdProxyURL), sdProxyURL, cacheFilesAge) vpnData := clirpc.FetchCachedServiceURL(cmd.Flags(), cacheFile(cacheDirSD, sdVpnURL), sdVpnURL, cacheFilesAge) visorData := clirpc.FetchCachedServiceURL(cmd.Flags(), cacheFile(cacheDirSD, sdVisorURL), sdVisorURL, cacheFilesAge) tpdData := clirpc.FetchCachedServiceURL(cmd.Flags(), cacheFile(cacheDirTPD, tpdFullURL), tpdFullURL, cacheFilesAge) // Parse service discovery data type sdEntry struct { Address string `json:"address"` Geo struct { Country string `json:"country"` } `json:"geo"` Version string `json:"version"` } var proxyEntries, vpnEntries, visorEntries []sdEntry json.Unmarshal([]byte(proxyData), &proxyEntries) json.Unmarshal([]byte(vpnData), &vpnEntries) json.Unmarshal([]byte(visorData), &visorEntries) // Build service map by public key type serviceInfo struct { PK string DisplayPK string Country string Version string Services []string } serviceMap := make(map[string]*serviceInfo) for _, e := range proxyEntries { pk := strings.Split(e.Address, ":")[0] if serviceMap[pk] == nil { serviceMap[pk] = &serviceInfo{PK: pk, Country: e.Geo.Country, Version: e.Version} } serviceMap[pk].Services = append(serviceMap[pk].Services, "proxy") } for _, e := range vpnEntries { pk := strings.Split(e.Address, ":")[0] if serviceMap[pk] == nil { serviceMap[pk] = &serviceInfo{PK: pk, Country: e.Geo.Country, Version: e.Version} } serviceMap[pk].Services = append(serviceMap[pk].Services, "vpn") if serviceMap[pk].Country == "" { serviceMap[pk].Country = e.Geo.Country } if serviceMap[pk].Version == "" { serviceMap[pk].Version = e.Version } } for _, e := range visorEntries { pk := strings.Split(e.Address, ":")[0] if serviceMap[pk] == nil { serviceMap[pk] = &serviceInfo{PK: pk, DisplayPK: e.Address, Country: e.Geo.Country, Version: e.Version} } else if serviceMap[pk].DisplayPK == "" { serviceMap[pk].DisplayPK = e.Address } serviceMap[pk].Services = append(serviceMap[pk].Services, "visor") if serviceMap[pk].Country == "" { serviceMap[pk].Country = e.Geo.Country } if serviceMap[pk].Version == "" { serviceMap[pk].Version = e.Version } } utData := clirpc.FetchCachedServiceURL(cmd.Flags(), cacheFile(cacheDirUT, utFullURL), utFullURL, cacheFilesAge) type utEntry struct { PK string `json:"pk"` On bool `json:"on"` } var utEntries []utEntry json.Unmarshal([]byte(utData), &utEntries) utStatus := make(map[string]string) for _, ut := range utEntries { if ut.On { utStatus[ut.PK] = "online" } else { utStatus[ut.PK] = "offline" } } if !noFilterOnline { for pk := range serviceMap { if utStatus[pk] != "online" { delete(serviceMap, pk) } } } // Parse transport data and count transports per key type tpEntry struct { Edges []string `json:"edges"` Type string `json:"type"` } var tpEntries []tpEntry json.Unmarshal([]byte(tpdData), &tpEntries) tpCountMap := make(map[string]*transportCounts) other := false for _, tp := range tpEntries { for _, edge := range tp.Edges { if tpCountMap[edge] == nil { tpCountMap[edge] = &transportCounts{} } if !tpCountMap[edge].add(tptypes.Type(tp.Type)) { other = true } } } // Combine data type networkEntry struct { PK string `json:"pk"` Country string `json:"country,omitempty"` Version string `json:"version,omitempty"` Services string `json:"services,omitempty"` transportCounts UTStatus string `json:"ut_status,omitempty"` // "online", "offline", or "" (not in UT) } var entries []networkEntry for pk, info := range serviceMap { services := strings.Join(info.Services, ",") counts := tpCountMap[pk] if counts == nil { counts = &transportCounts{} } if country != "" && info.Country != country { continue } if version != "" && info.Version != version { continue } if minTransports > 0 && counts.Total < minTransports { continue } displayPK := pk if info.DisplayPK != "" { displayPK = info.DisplayPK } entries = append(entries, networkEntry{ PK: displayPK, Country: info.Country, Version: info.Version, Services: services, transportCounts: *counts, UTStatus: utStatus[pk], }) } for pk, counts := range tpCountMap { if serviceMap[pk] != nil { continue } if !noFilterOnline && utStatus[pk] != "online" { continue } if minTransports > 0 && counts.Total < minTransports { continue } entries = append(entries, networkEntry{ PK: pk, transportCounts: *counts, UTStatus: utStatus[pk], }) } sort.Slice(entries, func(i, j int) bool { return entries[i].Total > entries[j].Total }) if jsonOutput { internal.PrintOutput(cmd.Flags(), entries, "") return } cmdutil.Printf("Legend: %s %s %s\n\n", pterm.Red("*offline"), pterm.BgRed.Sprint(pterm.White("*not in UT")), pterm.BgRed.Sprint(pterm.Black("*online <2 transports"))) // Use tabwriter for alignment var b bytes.Buffer w := tabwriter.NewWriter(&b, 0, 0, 3, ' ', tabwriter.TabIndent) fmt.Fprintln(w, "pk\tcountry\tversion\tservices\t"+transportColumns(other)+"\ttotal") for _, e := range entries { fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%d\n", e.PK, e.Country, e.Version, e.Services, e.row(other), e.Total) } w.Flush() lines := strings.Split(b.String(), "\n") for i, line := range lines { if line == "" { continue } if i == 0 { cmdutil.Println(line) continue } entryIdx := i - 1 if entryIdx >= len(entries) { cmdutil.Println(line) continue } e := entries[entryIdx] realTransports := e.direct() switch { case e.UTStatus == "": cmdutil.Println(pterm.BgRed.Sprint(pterm.White(line))) case e.UTStatus == "offline": cmdutil.Println(pterm.Red(line)) case e.UTStatus == "online" && realTransports < 2: cmdutil.Println(pterm.BgRed.Sprint(pterm.Black(line))) default: cmdutil.Println(line) } } }, }
RootCmd contains commands that interact with service discovery
Functions ¶
This section is empty.
Types ¶
This section is empty.
Click to show internal directories.
Click to hide internal directories.