Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve skywire-cli ut command logic #1679

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 110 additions & 43 deletions cmd/skywire-cli/commands/ut/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,28 @@ import (

"github.com/spf13/cobra"

"github.com/skycoin/dmsg/pkg/direct"
"github.com/skycoin/dmsg/pkg/disc"
"github.com/skycoin/dmsg/pkg/dmsg"
"github.com/skycoin/dmsg/pkg/dmsghttp"

"github.com/skycoin/skywire-utilities/pkg/cipher"
"github.com/skycoin/skywire-utilities/pkg/logging"
clirpc "github.com/skycoin/skywire/cmd/skywire-cli/commands/rpc"
"github.com/skycoin/skywire/cmd/skywire-cli/internal"
)

var (
pubkey cipher.PubKey
pk string
thisPk string
online bool
isStats bool
url string
pubkey cipher.PubKey
pk string
thisPk string
online bool
isStats bool
url string
data []byte
dmsgAddr string
dmsgIP string
utDmsgAddr string
)

var minUT int
Expand All @@ -33,7 +44,10 @@ func init() {
RootCmd.Flags().BoolVarP(&online, "on", "o", false, "list currently online visors")
RootCmd.Flags().BoolVarP(&isStats, "stats", "s", false, "count the number of results")
RootCmd.Flags().IntVarP(&minUT, "min", "n", 75, "list visors meeting minimum uptime")
RootCmd.Flags().StringVarP(&url, "url", "u", "", "specify alternative uptime tracker url\ndefault: http://ut.skywire.skycoin.com/uptimes?v=v2")
RootCmd.Flags().StringVarP(&url, "url", "u", "http://ut.skywire.skycoin.com/uptimes?v=v2", "specify alternative uptime tracker url\ndefault: http://ut.skywire.skycoin.com/uptimes?v=v2")
RootCmd.Flags().StringVar(&dmsgAddr, "dmsgAddr", "030c83534af1041aee60c2f124b682a9d60c6421876db7c67fc83a73c5effdbd96", "specific dmsg server address for dmsghttp query")
RootCmd.Flags().StringVar(&dmsgIP, "dmsgIP", "188.121.99.59:8081", "specific dmsg server ip for dmsghttp query")
RootCmd.Flags().StringVar(&utDmsgAddr, "utDmsgAddr", "dmsg://022c424caa6239ba7d1d9d8f7dab56cd5ec6ae2ea9ad97bb94ad4b48f62a540d3f:80", "dmsg address of uptime tracker")
}

// RootCmd contains commands that interact with the skywire-visor
Expand All @@ -42,50 +56,35 @@ var RootCmd = &cobra.Command{
Short: "query uptime tracker",
Long: "query uptime tracker\n Check local visor daily uptime percent with:\n skywire-cli ut -k $(skywire-cli visor pk)",
Run: func(cmd *cobra.Command, _ []string) {
if url == "" {
url = "http://ut.skywire.skycoin.com/uptimes?v=v2"
}
now := time.Now()
if pk != "" {
err := pubkey.Set(pk)
// tyring to connect with running visor
rpcClient, err := clirpc.Client(cmd.Flags())
if err != nil {
internal.PrintError(cmd.Flags(), fmt.Errorf("unable to create RPC client: %w", err))
} else {
data, err = rpcClient.FetchUptimeTrackerData(pk)
if err != nil {
internal.PrintFatalError(cmd.Flags(), fmt.Errorf("Invalid or missing public key"))
} else {
url += "&visors=" + pubkey.String()
internal.PrintError(cmd.Flags(), fmt.Errorf("unable to fetch uptime tracker data from RPC client: %w", err))
}
}
utClient := http.Client{
Timeout: time.Second * 15, // Timeout after 15 seconds
}

req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
log.Fatal(err)
}

res, getErr := utClient.Do(req)
if getErr != nil {
log.Fatal(getErr)
}

if res.Body != nil {
defer func() {
err := res.Body.Close()
if err != nil {
internal.PrintError(cmd.Flags(), fmt.Errorf("Failed to close response body"))
}
}()
// no rpc, so trying to dmsghttp query
if len(data) == 0 {
data, err = dmsgHTTPQuery(cmd)
if err != nil {
internal.PrintError(cmd.Flags(), fmt.Errorf("unable to fetch uptime tracker data in dmsgHTTPQuery method: %w", err))
}
}

body, readErr := io.ReadAll(res.Body)
if readErr != nil {
log.Fatal(readErr)
// nor rpc and dmsghttp, trying to direct http query
if len(data) == 0 {
data, err = httpQuery(cmd)
if err != nil {
internal.PrintFatalError(cmd.Flags(), fmt.Errorf("unable to fetch uptime tracker data in httpQuery method: %w", err))
}
}

now := time.Now()
startDate := time.Date(now.Year(), now.Month(), -1, 0, 0, 0, 0, now.Location()).Format("2006-01-02")
endDate := time.Date(now.Year(), now.Month()+1, 1, 0, 0, 0, 0, now.Location()).Add(-1 * time.Second).Format("2006-01-02")
uts := uptimes{}
jsonErr := json.Unmarshal(body, &uts)
jsonErr := json.Unmarshal(data, &uts)
if jsonErr != nil {
log.Fatal(jsonErr)
}
Expand Down Expand Up @@ -128,6 +127,74 @@ func selectedDaily(data map[string]string, startDate, endDate string) {
}
}

func dmsgHTTPQuery(cmd *cobra.Command) ([]byte, error) {
pk, sk := cipher.GenerateKeyPair()
var dmsgAddrPK cipher.PubKey
err := dmsgAddrPK.Set(dmsgAddr)
if err != nil {
return []byte{}, err
}

servers := []*disc.Entry{{Server: &disc.Server{Address: dmsgIP}, Static: dmsgAddrPK}}
keys := cipher.PubKeys{pk}

entries := direct.GetAllEntries(keys, servers)
dClient := direct.NewClient(entries, logging.NewMasterLogger().PackageLogger("ut_dmsgHTTPQuery"))

dmsgDC, closeDmsgDC, err := direct.StartDmsg(cmd.Context(), logging.NewMasterLogger().PackageLogger("ut_dmsgHTTPQuery"),
pk, sk, dClient, dmsg.DefaultConfig())
if err != nil {
return []byte{}, fmt.Errorf("failed to start dmsg: %w", err)
}
defer closeDmsgDC()

dmsgHTTP := http.Client{Transport: dmsghttp.MakeHTTPTransport(cmd.Context(), dmsgDC)}

resp, err := dmsgHTTP.Get(utDmsgAddr + "/uptimes?v=v2")
if err != nil {
return []byte{}, err
}
return io.ReadAll(resp.Body)
}

func httpQuery(cmd *cobra.Command) ([]byte, error) {
if pk != "" {
err := pubkey.Set(pk)
if err != nil {
return []byte{}, err
}
url += "&visors=" + pubkey.String()
}
utClient := http.Client{
Timeout: time.Second * 15, // Timeout after 15 seconds
}

req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return []byte{}, err
}

res, err := utClient.Do(req)
if err != nil {
return []byte{}, err
}

if res.Body != nil {
defer func() {
err := res.Body.Close()
if err != nil {
internal.PrintError(cmd.Flags(), fmt.Errorf("Failed to close response body"))
}
}()
}

body, err := io.ReadAll(res.Body)
if err != nil {
return []byte{}, err
}
return body, nil
}

type uptimes []struct {
Pk string `json:"pk"`
Up int `json:"up"`
Expand Down
2 changes: 1 addition & 1 deletion dmsghttp-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
{
"static": "030c83534af1041aee60c2f124b682a9d60c6421876db7c67fc83a73c5effdbd96",
"server": {
"address": "103.216.61.169:8081"
"address": "188.121.99.59:8081"
}
}
],
Expand Down
26 changes: 26 additions & 0 deletions pkg/utclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"bytes"
"context"
"fmt"
"io"
"net/http"
"time"

Expand All @@ -19,6 +20,7 @@ import (
// APIClient implements uptime tracker API client.
type APIClient interface {
UpdateVisorUptime(context.Context, string) error
FetchUptimes(context.Context, string) ([]byte, error)
}

// httpClient implements Client for uptime tracker API.
Expand Down Expand Up @@ -97,3 +99,27 @@ func (c *httpClient) UpdateVisorUptime(ctx context.Context, version string) erro

return nil
}

// FetchUptimes fetch uptimes data for all visors or specific one
func (c *httpClient) FetchUptimes(ctx context.Context, pk string) ([]byte, error) {
url := "/uptimes?v=v2"
if pk != "" {
url += "&visors=" + pk
}

resp, err := c.Get(ctx, url)
if err != nil {
return []byte{}, err
}

if resp.Body != nil {
defer func() {
err = resp.Body.Close()
if err != nil {
return
}
}()
}

return io.ReadAll(resp.Body)
}
20 changes: 20 additions & 0 deletions pkg/utclient/mock_api_client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions pkg/visor/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ type API interface {
StopPing(pk cipher.PubKey) error

TestVisor(config PingConfig) ([]TestResult, error)

//uptime-tracker tools
FetchUptimeTrackerData(pk string) ([]byte, error)
}

// HealthCheckable resource returns its health status as an integer
Expand Down Expand Up @@ -550,6 +553,23 @@ func (v *Visor) StopVPNClient(appName string) error {
return ErrProcNotAvailable
}

// FetchUptimeTrackerData implements API
func (v *Visor) FetchUptimeTrackerData(pk string) ([]byte, error) {
var body []byte
var pubkey cipher.PubKey

if pk != "" {
err := pubkey.Set(pk)
if err != nil {
return body, fmt.Errorf("Invalid or missing public key")
}
}
if v.uptimeTracker == nil {
return body, fmt.Errorf("Uptime tracker module not available")
}
return v.uptimeTracker.FetchUptimes(context.TODO(), pk)
}

// StartSkysocksClient implements API.
func (v *Visor) StartSkysocksClient(serverKey string) error {
var envs []string
Expand Down
8 changes: 8 additions & 0 deletions pkg/visor/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,14 @@ func (r *RPC) StopVPNClient(name *string, _ *struct{}) (err error) {
return r.visor.StopVPNClient(*name)
}

// FetchUptimeTrackerData trying to fetch ut data
func (r *RPC) FetchUptimeTrackerData(pk string, data *[]byte) (err error) {
defer rpcutil.LogCall(r.log, "FetchUptimeTrackerData", pk)(data, &err)
rep, err := r.visor.FetchUptimeTrackerData(pk)
*data = rep
return err
}

// StartSkysocksClient starts SkysocksClient App
func (r *RPC) StartSkysocksClient(pk string, _ *struct{}) (err error) {
defer rpcutil.LogCall(r.log, "StartSkysocksClient", pk)(nil, &err)
Expand Down
12 changes: 12 additions & 0 deletions pkg/visor/rpc_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,13 @@ func (rc *rpcClient) StopVPNClient(appName string) error {
return rc.Call("StopVPNClient", &appName, &struct{}{})
}

// FetchUptimeTrackerData calls FetchUptimeTrackerData.
func (rc *rpcClient) FetchUptimeTrackerData(pk string) ([]byte, error) {
var data []byte
err := rc.Call("FetchUptimeTrackerData", pk, &data)
return data, err
}

// StartSkysocksClient calls StartSkysocksClient.
func (rc *rpcClient) StartSkysocksClient(pk string) error {
return rc.Call("StartSkysocksClient", pk, &struct{}{})
Expand Down Expand Up @@ -852,6 +859,11 @@ func (*mockRPCClient) StopVPNClient(string) error {
return nil
}

// FetchUptimeTrackerData implements API.
func (*mockRPCClient) FetchUptimeTrackerData(string) ([]byte, error) {
return []byte{}, nil
}

// StartSkysocksClient implements API.
func (*mockRPCClient) StartSkysocksClient(string) error {
return nil
Expand Down
Loading