From 5dc0abaf6f3d0d2a19e35a91e9b33b6f2dadb53b Mon Sep 17 00:00:00 2001 From: Mohammed <79150699+mrpalide@users.noreply.github.com> Date: Sat, 9 Dec 2023 15:36:13 +0000 Subject: [PATCH] improve `skywire-cli ut` command logic (#1679) * initial commit for improve skywire-cli ut command logic * fix panic issue * fix lint issue --- cmd/skywire-cli/commands/ut/root.go | 153 ++++++++++++++++++++-------- dmsghttp-config.json | 2 +- pkg/utclient/client.go | 26 +++++ pkg/utclient/mock_api_client.go | 20 ++++ pkg/visor/api.go | 20 ++++ pkg/visor/rpc.go | 8 ++ pkg/visor/rpc_client.go | 12 +++ 7 files changed, 197 insertions(+), 44 deletions(-) diff --git a/cmd/skywire-cli/commands/ut/root.go b/cmd/skywire-cli/commands/ut/root.go index ad1534f22c..732c76d03d 100644 --- a/cmd/skywire-cli/commands/ut/root.go +++ b/cmd/skywire-cli/commands/ut/root.go @@ -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 @@ -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 @@ -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) } @@ -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"` diff --git a/dmsghttp-config.json b/dmsghttp-config.json index 9de4147b0d..a2e3c9f5a9 100644 --- a/dmsghttp-config.json +++ b/dmsghttp-config.json @@ -74,7 +74,7 @@ { "static": "030c83534af1041aee60c2f124b682a9d60c6421876db7c67fc83a73c5effdbd96", "server": { - "address": "103.216.61.169:8081" + "address": "188.121.99.59:8081" } } ], diff --git a/pkg/utclient/client.go b/pkg/utclient/client.go index ddc742fa73..eb2fbbbf2f 100644 --- a/pkg/utclient/client.go +++ b/pkg/utclient/client.go @@ -5,6 +5,7 @@ import ( "bytes" "context" "fmt" + "io" "net/http" "time" @@ -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. @@ -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) +} diff --git a/pkg/utclient/mock_api_client.go b/pkg/utclient/mock_api_client.go index d22c3267da..422375e5a3 100644 --- a/pkg/utclient/mock_api_client.go +++ b/pkg/utclient/mock_api_client.go @@ -47,3 +47,23 @@ func (_m *MockAPIClient) UpdateVisorUptime(_a0 context.Context, version string) return r0 } + +// FetchUptimes provide a mock function with given field :_a0 +func (_m *MockAPIClient) FetchUptimes(ctx context.Context, pk string) ([]byte,error){ + ret := _m.Called(ctx) + + var r0 []byte + if rf, ok := ret.Get(0).(func(context.Context) []byte); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).([]byte) + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} diff --git a/pkg/visor/api.go b/pkg/visor/api.go index d829a94dc8..790d27329d 100644 --- a/pkg/visor/api.go +++ b/pkg/visor/api.go @@ -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 @@ -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 diff --git a/pkg/visor/rpc.go b/pkg/visor/rpc.go index a292b40733..6cde0f716b 100644 --- a/pkg/visor/rpc.go +++ b/pkg/visor/rpc.go @@ -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) diff --git a/pkg/visor/rpc_client.go b/pkg/visor/rpc_client.go index be561480d6..a6aff2d71b 100644 --- a/pkg/visor/rpc_client.go +++ b/pkg/visor/rpc_client.go @@ -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{}{}) @@ -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