From 04ade12d37337361c1df92378a44bfd2dafea5bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BF=97=E5=AE=87?= Date: Mon, 24 Feb 2020 22:27:41 +0800 Subject: [PATCH] Paused progress on hypervisor endpoints refactor. The following has been commented out: * WIP: Visor endpoints exposed for hypervisor switched to RESTful. * WIP: Various new helper functions for having http over dmsg. --- go.mod | 3 +- go.sum | 4 +- pkg/httputil/httputil.go | 86 ---- pkg/hypervisor/config.go | 3 +- pkg/hypervisor/hypervisor.go | 34 +- pkg/hypervisor/user_manager.go | 4 +- pkg/visor/gateway.go | 414 ++++++++++++++++++ pkg/visor/rpc.go | 5 +- pkg/visor/rpc_test.go | 10 +- pkg/visor/visor.go | 17 +- .../SkycoinProject/dmsg/httputil/httputil.go | 13 + vendor/modules.txt | 2 +- 12 files changed, 464 insertions(+), 131 deletions(-) delete mode 100644 pkg/httputil/httputil.go create mode 100644 pkg/visor/gateway.go diff --git a/go.mod b/go.mod index af13a7a1d6..95730b37e6 100644 --- a/go.mod +++ b/go.mod @@ -3,13 +3,12 @@ module github.com/SkycoinProject/skywire-mainnet go 1.13 require ( - github.com/SkycoinProject/dmsg v0.0.0-20200220122410-79d9d7bac617 + github.com/SkycoinProject/dmsg v0.0.0-20200224064625-1b539081519c github.com/SkycoinProject/skycoin v0.27.0 github.com/SkycoinProject/yamux v0.0.0-20191213015001-a36efeefbf6a github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 github.com/go-chi/chi v4.0.2+incompatible github.com/google/uuid v1.1.1 - github.com/gorilla/handlers v1.4.2 github.com/gorilla/securecookie v1.1.1 github.com/hashicorp/yamux v0.0.0-20190923154419-df201c70410d github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect diff --git a/go.sum b/go.sum index 9c86c8a129..5efe75dbab 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/SkycoinProject/dmsg v0.0.0-20200220122410-79d9d7bac617 h1:dTlZiB/kaSMezzwyEsOs2fjbbDkgoLzdLSwh7GmOIK4= -github.com/SkycoinProject/dmsg v0.0.0-20200220122410-79d9d7bac617/go.mod h1:eCoemDDyfyfNTFrapYKNEItwtRIj54UGpu4Ffcznuds= +github.com/SkycoinProject/dmsg v0.0.0-20200224064625-1b539081519c h1:TBwm7dzyUYnOG/Ycb3HBh7JshQavePHHfh5NOAzlNww= +github.com/SkycoinProject/dmsg v0.0.0-20200224064625-1b539081519c/go.mod h1:eCoemDDyfyfNTFrapYKNEItwtRIj54UGpu4Ffcznuds= github.com/SkycoinProject/skycoin v0.26.0 h1:8/ZRZb2VM2DM4YTIitRJMZ3Yo/3H1FFmbCMx5o6ekmA= github.com/SkycoinProject/skycoin v0.26.0/go.mod h1:xqPLOKh5B6GBZlGA7B5IJfQmCy7mwimD9NlqxR3gMXo= github.com/SkycoinProject/skycoin v0.27.0 h1:N3IHxj8ossHOcsxLYOYugT+OaELLncYHJHxbbYLPPmY= diff --git a/pkg/httputil/httputil.go b/pkg/httputil/httputil.go deleted file mode 100644 index e299a9c8fb..0000000000 --- a/pkg/httputil/httputil.go +++ /dev/null @@ -1,86 +0,0 @@ -package httputil - -import ( - "encoding/json" - "fmt" - "io" - "net" - "net/http" - "strconv" - "strings" - - "github.com/SkycoinProject/skycoin/src/util/logging" - "github.com/gorilla/handlers" -) - -var log = logging.MustGetLogger("httputil") - -// WriteJSON writes a json object on a http.ResponseWriter with the given code, -// panics on marshaling error -func WriteJSON(w http.ResponseWriter, r *http.Request, code int, v interface{}) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(code) - enc := json.NewEncoder(w) - pretty, err := BoolFromQuery(r, "pretty", false) - if err != nil { - log.WithError(err).Warn("Failed to get bool from query") - } - if pretty { - enc.SetIndent("", " ") - } - if err, ok := v.(error); ok { - v = map[string]interface{}{"error": err.Error()} - } - if err := json.NewEncoder(w).Encode(v); err != nil { - panic(err) - } -} - -// ReadJSON reads the request body to a json object. -func ReadJSON(r *http.Request, v interface{}) error { - dec := json.NewDecoder(r.Body) - dec.DisallowUnknownFields() - return dec.Decode(v) -} - -// BoolFromQuery obtains a boolean from a query entry. -func BoolFromQuery(r *http.Request, key string, defaultVal bool) (bool, error) { - switch q := r.URL.Query().Get(key); q { - case "true", "on", "1": - return true, nil - case "false", "off", "0": - return false, nil - case "": - return defaultVal, nil - default: - return false, fmt.Errorf("invalid '%s' query value of '%s'", key, q) - } -} - -// WriteLog writes request and response parameters using format that -// works well with logging.Logger. -func WriteLog(writer io.Writer, params handlers.LogFormatterParams) { - host, _, err := net.SplitHostPort(params.Request.RemoteAddr) - if err != nil { - host = params.Request.RemoteAddr - } - - _, err = fmt.Fprintf( - writer, "%s - \"%s %s %s\" %d\n", - host, params.Request.Method, params.URL.String(), params.Request.Proto, params.StatusCode, - ) - if err != nil { - log.WithError(err).Warn("Failed to write log") - } -} - -// SplitRPCAddr returns host and port and whatever error results from parsing the rpc address interface -func SplitRPCAddr(rpcAddr string) (host string, port uint16, err error) { - addrToken := strings.Split(rpcAddr, ":") - uint64port, err := strconv.ParseUint(addrToken[1], 10, 16) - if err != nil { - return - } - - return addrToken[0], uint16(uint64port), nil -} diff --git a/pkg/hypervisor/config.go b/pkg/hypervisor/config.go index f377d553f1..224158aaf7 100644 --- a/pkg/hypervisor/config.go +++ b/pkg/hypervisor/config.go @@ -10,8 +10,9 @@ import ( "github.com/SkycoinProject/dmsg/cipher" + "github.com/SkycoinProject/dmsg/httputil" + "github.com/SkycoinProject/skywire-mainnet/internal/skyenv" - "github.com/SkycoinProject/skywire-mainnet/pkg/httputil" "github.com/SkycoinProject/skywire-mainnet/pkg/util/pathutil" ) diff --git a/pkg/hypervisor/hypervisor.go b/pkg/hypervisor/hypervisor.go index 62191fac81..976052abfe 100644 --- a/pkg/hypervisor/hypervisor.go +++ b/pkg/hypervisor/hypervisor.go @@ -16,13 +16,13 @@ import ( "github.com/SkycoinProject/dmsg" "github.com/SkycoinProject/dmsg/cipher" + "github.com/SkycoinProject/dmsg/httputil" "github.com/SkycoinProject/skycoin/src/util/logging" "github.com/go-chi/chi" "github.com/go-chi/chi/middleware" "github.com/google/uuid" "github.com/SkycoinProject/skywire-mainnet/pkg/app" - "github.com/SkycoinProject/skywire-mainnet/pkg/httputil" "github.com/SkycoinProject/skywire-mainnet/pkg/routing" "github.com/SkycoinProject/skywire-mainnet/pkg/visor" ) @@ -168,7 +168,6 @@ func (m *Hypervisor) ServeHTTP(w http.ResponseWriter, req *http.Request) { r.Delete("/visors/{pk}/routes/{rid}", m.deleteRoute()) r.Get("/visors/{pk}/loops", m.getLoops()) r.Get("/visors/{pk}/restart", m.restart()) - r.Post("/visors/{pk}/exec", m.exec()) }) }) @@ -228,37 +227,6 @@ func (m *Hypervisor) getUptime() http.HandlerFunc { }) } -// executes a command and returns its output -func (m *Hypervisor) exec() http.HandlerFunc { - return m.withCtx(m.visorCtx, func(w http.ResponseWriter, r *http.Request, ctx *httpCtx) { - var reqBody struct { - Command string `json:"command"` - } - - if err := httputil.ReadJSON(r, &reqBody); err != nil { - if err != io.EOF { - log.Warnf("exec request: %v", err) - } - - httputil.WriteJSON(w, r, http.StatusBadRequest, ErrMalformedRequest) - - return - } - - out, err := ctx.RPC.Exec(reqBody.Command) - if err != nil { - httputil.WriteJSON(w, r, http.StatusInternalServerError, err) - return - } - - output := struct { - Output string `json:"output"` - }{string(out)} - - httputil.WriteJSON(w, r, http.StatusOK, output) - }) -} - type summaryResp struct { TCPAddr string `json:"tcp_addr"` Online bool `json:"online"` diff --git a/pkg/hypervisor/user_manager.go b/pkg/hypervisor/user_manager.go index afb0913350..56515c5d19 100644 --- a/pkg/hypervisor/user_manager.go +++ b/pkg/hypervisor/user_manager.go @@ -12,7 +12,7 @@ import ( "github.com/google/uuid" "github.com/gorilla/securecookie" - "github.com/SkycoinProject/skywire-mainnet/pkg/httputil" + "github.com/SkycoinProject/dmsg/httputil" ) const ( @@ -30,6 +30,8 @@ var ( ErrUserNotFound = errors.New("user is either deleted or not found") ) +// Other errors + // for use with context.Context type ctxKey string diff --git a/pkg/visor/gateway.go b/pkg/visor/gateway.go new file mode 100644 index 0000000000..3d677777cd --- /dev/null +++ b/pkg/visor/gateway.go @@ -0,0 +1,414 @@ +package visor + +/* + !!! DO NOT DELETE !!! + TODO(evanlinjin): This is taking far too long, we will leave this to be completed later. +*/ + +//// App constants. +//const ( +// statusStop = iota +// statusStart +//) +// +//type Gateway struct { +// v *Visor +//} +// +///* +// <<< VISOR ENDPOINTS >>> +//*/ +// +//func handleGetHealth(v *Visor) http.HandlerFunc { +// return func(w http.ResponseWriter, r *http.Request) { +// hi := HealthInfo{ +// TransportDiscovery: http.StatusOK, +// RouteFinder: http.StatusOK, +// SetupNode: http.StatusOK, +// } +// if _, err := v.conf.TransportDiscovery(); err != nil { +// hi.TransportDiscovery = http.StatusNotFound +// } +// if v.conf.Routing.RouteFinder == "" { +// hi.RouteFinder = http.StatusNotFound +// } +// if len(v.conf.Routing.SetupNodes) == 0 { +// hi.SetupNode = http.StatusNotFound +// } +// httputil.WriteJSON(w, r, http.StatusOK, hi) +// } +//} +// +//func handleGetUptime(v *Visor) http.HandlerFunc { +// return func(w http.ResponseWriter, r *http.Request) { +// uptime := time.Since(v.startedAt).Seconds() +// httputil.WriteJSON(w, r, http.StatusOK, uptime) +// } +//} +// +//func handleGetSummary(v *Visor) http.HandlerFunc { +// return func(w http.ResponseWriter, r *http.Request) { +// httputil.WriteJSON(w, r, http.StatusOK, makeVisorSummary(v)) +// } +//} +// +///* +// <<< APP ENDPOINTS >>> +//*/ +// +//func handleGetApps(v *Visor) http.HandlerFunc { +// return func(w http.ResponseWriter, r *http.Request) { +// httputil.WriteJSON(w, r, http.StatusOK, v.Apps()) +// } +//} +// +//func handleGetApp(v *Visor) http.HandlerFunc { +// return func(w http.ResponseWriter, r *http.Request) { +// appState, ok := httpAppState(v, w, r) +// if !ok { +// return +// } +// httputil.WriteJSON(w, r, http.StatusOK, appState) +// } +//} +// +//// TODO: simplify +//// nolint: funlen,gocognit,godox +//func handlePutApp(v *Visor) http.HandlerFunc { +// return func(w http.ResponseWriter, r *http.Request) { +// appS, ok := httpAppState(v, w, r) +// if !ok { +// return +// } +// var reqBody struct { +// AutoStart *bool `json:"autostart,omitempty"` +// Status *int `json:"status,omitempty"` +// Passcode *string `json:"passcode,omitempty"` +// PK *cipher.PubKey `json:"pk,omitempty"` +// } +// if err := httputil.ReadJSON(r, &reqBody); err != nil { +// if err != io.EOF { +// log.Warnf("handlePutApp request: %v", err) +// } +// httputil.WriteJSON(w, r, http.StatusBadRequest, +// fmt.Errorf("failed to read JSON from http request body: %v", err)) +// return +// } +// +// if reqBody.AutoStart != nil { +// if *reqBody.AutoStart != appS.AutoStart { +// if err := v.setAutoStart(appS.Name, *reqBody.AutoStart); err != nil { +// httputil.WriteJSON(w, r, http.StatusInternalServerError, err) +// return +// } +// } +// } +// +// if reqBody.Status != nil { +// switch *reqBody.Status { +// case statusStop: +// if err := v.StopApp(appS.Name); err != nil { +// httputil.WriteJSON(w, r, http.StatusInternalServerError, err) +// return +// } +// case statusStart: +// if err := v.StartApp(appS.Name); err != nil { +// httputil.WriteJSON(w, r, http.StatusInternalServerError, err) +// return +// } +// default: +// errMsg := fmt.Errorf("value of 'status' field is %d when expecting 0 or 1", *reqBody.Status) +// httputil.WriteJSON(w, r, http.StatusBadRequest, errMsg) +// return +// } +// } +// +// const ( +// skysocksName = "skysocks" +// skysocksClientName = "skysocks-client" +// ) +// +// if reqBody.Passcode != nil && appS.Name == skysocksName { +// if err := v.setSocksPassword(*reqBody.Passcode); err != nil { +// httputil.WriteJSON(w, r, http.StatusInternalServerError, err) +// return +// } +// } +// +// if reqBody.PK != nil && appS.Name == skysocksClientName { +// if err := v.setSocksClientPK(*reqBody.PK); err != nil { +// httputil.WriteJSON(w, r, http.StatusInternalServerError, err) +// return +// } +// } +// +// appS, _ = v.App(appS.Name) +// httputil.WriteJSON(w, r, http.StatusOK, appS) +// } +//} +// +//// AppLogsResp parses logs as json, along with the last obtained timestamp for use on subsequent requests +//type AppLogsResp struct { +// LastLogTimestamp string `json:"last_log_timestamp"` +// Logs []string `json:"logs"` +//} +// +//func handleGetAppLogsSince(v *Visor) http.HandlerFunc { +// return func(w http.ResponseWriter, r *http.Request) { +// appS, ok := httpAppState(v, w, r) +// if !ok { +// return +// } +// +// since := r.URL.Query().Get("since") +// since = strings.Replace(since, " ", "+", 1) // we need to put '+' again that was replaced in the query string +// +// // if time is not parsable or empty default to return all logs +// t, err := time.Parse(time.RFC3339Nano, since) +// if err != nil { +// t = time.Unix(0, 0) +// } +// +// ls, err := app.NewLogStore(filepath.Join(v.dir(), appS.Name), appS.Name, "bbolt") +// if err != nil { +// httputil.WriteJSON(w, r, http.StatusInternalServerError, err) +// return +// } +// logs, err := ls.LogsSince(t) +// if err != nil { +// httputil.WriteJSON(w, r, http.StatusInternalServerError, err) +// return +// } +// if len(logs) == 0 { +// httputil.WriteJSON(w, r, http.StatusServiceUnavailable, err) +// return +// } +// httputil.WriteJSON(w, r, http.StatusOK, &AppLogsResp{ +// LastLogTimestamp: app.TimestampFromLog(logs[len(logs)-1]), +// Logs: logs, +// }) +// } +//} +// +///* +// <<< TRANSPORT ENDPOINTS >>> +//*/ +// +//func handleTransportTypes(v *Visor) http.HandlerFunc { +// return func(w http.ResponseWriter, r *http.Request) { +// httputil.WriteJSON(w, r, http.StatusOK, v.tm.Networks()) +// } +//} +// +//func handleGetTransport(v *Visor) http.HandlerFunc { +// return func(w http.ResponseWriter, r *http.Request) { +// tp, ok := httpTransport(v, w, r) +// if !ok { +// return +// } +// httputil.WriteJSON(w, r, http.StatusOK, +// newTransportSummary(v.tm, tp, true, v.router.SetupIsTrusted(tp.Remote()))) +// } +//} +// +//func handleGetTransports(v *Visor) http.HandlerFunc { +// return func(w http.ResponseWriter, r *http.Request) { +// qTypes := strSliceFromQuery(r, "type", nil) +// +// qPKs, err := pkSliceFromQuery(r, "pk", nil) +// if err != nil { +// httputil.WriteJSON(w, r, http.StatusBadRequest, err) +// return +// } +// +// qLogs, err := httputil.BoolFromQuery(r, "logs", true) +// if err != nil { +// httputil.WriteJSON(w, r, http.StatusBadRequest, err) +// return +// } +// +// tps, err := listTransports(v, TransportsIn{ +// FilterTypes:qTypes, +// FilterPubKeys:qPKs, +// ShowLogs:qLogs, +// }) +// if err != nil { +// httputil.WriteJSON(w, r, http.StatusInternalServerError, err) +// return +// } +// httputil.WriteJSON(w, r, http.StatusOK, tps) +// } +//} +// +//type PostTransportReq struct { +// TpType string `json:"transport_type"` +// Remote cipher.PubKey `json:"remote_pk"` +// Public bool `json:"public"` +//} +// +//func handlePostTransport(v *Visor) http.HandlerFunc { +// return func(w http.ResponseWriter, r *http.Request) { +// var reqB PostTransportReq +// if err := httputil.ReadJSON(r, &reqB); err != nil { +// if err != io.EOF { +// log.Warnf("handlePostTransport request: %v", err) +// } +// httputil.WriteJSON(w, r, http.StatusBadRequest, +// fmt.Errorf("failed to read JSON from http request body: %v", err)) +// return +// } +// mTp, err := v.tm.SaveTransport(r.Context(), reqB.Remote, reqB.TpType) +// if err != nil { +// httputil.WriteJSON(w, r, http.StatusInternalServerError, err) +// return +// } +// httputil.WriteJSON(w, r, http.StatusOK, +// newTransportSummary(v.tm, mTp, false, v.router.SetupIsTrusted(mTp.Remote()))) +// } +//} +// +//func handleDelTransport(v *Visor) http.HandlerFunc { +// return func(w http.ResponseWriter, r *http.Request) { +// tp, ok := httpTransport(v, w, r) +// if !ok { +// return +// } +// v.tm.DeleteTransport(tp.Entry.ID) +// } +//} +// +///* +// <<< ROUTER ENDPOINTS >>> +//*/ +// +// +///* +// <<< HELPER FUNCTIONS >>> +//*/ +// +//func httpAppState(v *Visor, w http.ResponseWriter, r *http.Request) (*AppState, bool) { +// appName := chi.URLParam(r, "app") +// +// appState, ok := v.App(appName) +// if !ok { +// httputil.WriteJSON(w, r, http.StatusNotFound, +// fmt.Sprintf("app of name %s is not found in visor", appName)) +// return nil, false +// } +// return appState, true +//} +// +//func httpTransport(v *Visor, w http.ResponseWriter, r *http.Request) (*transport.ManagedTransport, bool) { +// tid, err := uuidFromParam(r, "tid") +// if err != nil { +// httputil.WriteJSON(w, r, http.StatusBadRequest, err) +// return nil, false +// } +// tp := v.tm.Transport(tid) +// if tp == nil { +// httputil.WriteJSON(w, r, http.StatusNotFound, +// fmt.Errorf("transport of ID %v is not found", tid)) +// return nil, false +// } +// return tp, true +//} +// +//func httpRoute(v *Visor, w http.ResponseWriter, r *http.Request) (routing.RouteID, bool) { +// rid, err := ridFromParam(r, "rid") +// if err != nil { +// httputil.WriteJSON(w, r, http.StatusBadRequest, err) +// return rid, false +// } +// return rid, true +//} +// +//func makeVisorSummary(v *Visor) *Summary { +// var tpSums []*TransportSummary +// v.tm.WalkTransports(func(tp *transport.ManagedTransport) bool { +// isSetup := v.router.SetupIsTrusted(tp.Remote()) +// tpSums = append(tpSums, newTransportSummary(v.tm, tp, true, isSetup)) +// return true +// }) +// return &Summary{ +// PubKey: v.conf.Visor.StaticPubKey, +// BuildInfo: buildinfo.Get(), +// AppProtoVersion: supportedProtocolVersion, +// Apps: v.Apps(), +// Transports: tpSums, +// RoutesCount: v.rt.Count(), +// } +//} +// +//func uuidFromParam(r *http.Request, key string) (uuid.UUID, error) { +// return uuid.Parse(chi.URLParam(r, key)) +//} +// +//func ridFromParam(r *http.Request, key string) (routing.RouteID, error) { +// rid, err := strconv.ParseUint(chi.URLParam(r, key), 10, 32) +// if err != nil { +// return 0, errors.New("invalid route ID provided") +// } +// +// return routing.RouteID(rid), nil +//} +// +//func strSliceFromQuery(r *http.Request, key string, defaultVal []string) []string { +// slice, ok := r.URL.Query()[key] +// if !ok { +// return defaultVal +// } +// +// return slice +//} +// +//func pkSliceFromQuery(r *http.Request, key string, defaultVal []cipher.PubKey) ([]cipher.PubKey, error) { +// qPKs, ok := r.URL.Query()[key] +// if !ok { +// return defaultVal, nil +// } +// +// pks := make([]cipher.PubKey, len(qPKs)) +// +// for i, qPK := range qPKs { +// pk := cipher.PubKey{} +// if err := pk.UnmarshalText([]byte(qPK)); err != nil { +// return nil, err +// } +// +// pks[i] = pk +// } +// return pks, nil +//} +// +//func listTransports(v *Visor, in TransportsIn) ([]*TransportSummary, error) { +// typeIncluded := func(tType string) bool { +// if in.FilterTypes != nil { +// for _, ft := range in.FilterTypes { +// if tType == ft { +// return true +// } +// } +// return false +// } +// return true +// } +// pkIncluded := func(localPK, remotePK cipher.PubKey) bool { +// if in.FilterPubKeys != nil { +// for _, fpk := range in.FilterPubKeys { +// if localPK == fpk || remotePK == fpk { +// return true +// } +// } +// return false +// } +// return true +// } +// var tps []*TransportSummary +// v.tm.WalkTransports(func(tp *transport.ManagedTransport) bool { +// if typeIncluded(tp.Type()) && pkIncluded(v.tm.Local(), tp.Remote()) { +// tps = append(tps, newTransportSummary(v.tm, tp, in.ShowLogs, v.router.SetupIsTrusted(tp.Remote()))) +// } +// return true +// }) +// return tps, nil +//} diff --git a/pkg/visor/rpc.go b/pkg/visor/rpc.go index ff459d5d49..e501bf5fcc 100644 --- a/pkg/visor/rpc.go +++ b/pkg/visor/rpc.go @@ -126,8 +126,7 @@ type TransportSummary struct { IsSetup bool `json:"is_setup"` } -func newTransportSummary(tm *transport.Manager, tp *transport.ManagedTransport, - includeLogs bool, isSetup bool) *TransportSummary { +func newTransportSummary(tm *transport.Manager, tp *transport.ManagedTransport, incLogs bool, isSetup bool) *TransportSummary { summary := &TransportSummary{ ID: tp.Entry.ID, @@ -136,7 +135,7 @@ func newTransportSummary(tm *transport.Manager, tp *transport.ManagedTransport, Type: tp.Type(), IsSetup: isSetup, } - if includeLogs { + if incLogs { summary.Log = tp.LogEntry } return summary diff --git a/pkg/visor/rpc_test.go b/pkg/visor/rpc_test.go index 3a4c8785e2..f6444843b3 100644 --- a/pkg/visor/rpc_test.go +++ b/pkg/visor/rpc_test.go @@ -2,6 +2,7 @@ package visor import ( "fmt" + "io/ioutil" "net/http" "os" "path/filepath" @@ -121,6 +122,10 @@ func TestListApps(t *testing.T) { } func TestStartStopApp(t *testing.T) { + tempDir, err := ioutil.TempDir(os.TempDir(), "") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(tempDir)) }() + pk, _ := cipher.GenerateKeyPair() r := &router.MockRouter{} r.On("Serve", mock.Anything /* context */).Return(testhelpers.NoErr) @@ -180,7 +185,7 @@ func TestStartStopApp(t *testing.T) { rpc := &RPC{visor: visor} - err := rpc.StartApp(&unknownApp, nil) + err = rpc.StartApp(&unknownApp, nil) require.Error(t, err) assert.Equal(t, ErrUnknownApp, err) @@ -193,6 +198,9 @@ func TestStartStopApp(t *testing.T) { require.NoError(t, rpc.StopApp(&app, nil)) time.Sleep(100 * time.Millisecond) + + // remove files + require.NoError(t, os.RemoveAll("foo")) } /* diff --git a/pkg/visor/visor.go b/pkg/visor/visor.go index 8e94b38c3c..7b8bdd9d49 100644 --- a/pkg/visor/visor.go +++ b/pkg/visor/visor.go @@ -25,10 +25,11 @@ import ( "github.com/SkycoinProject/dmsg/dmsgpty" "github.com/SkycoinProject/skycoin/src/util/logging" + "github.com/SkycoinProject/dmsg/httputil" + "github.com/SkycoinProject/skywire-mainnet/pkg/app/appcommon" "github.com/SkycoinProject/skywire-mainnet/pkg/app/appnet" "github.com/SkycoinProject/skywire-mainnet/pkg/app/appserver" - "github.com/SkycoinProject/skywire-mainnet/pkg/httputil" "github.com/SkycoinProject/skywire-mainnet/pkg/restart" "github.com/SkycoinProject/skywire-mainnet/pkg/routefinder/rfclient" "github.com/SkycoinProject/skywire-mainnet/pkg/router" @@ -297,6 +298,7 @@ func (visor *Visor) Start() error { }(ac) } + // CLI and RPC server. rpcSvr := rpc.NewServer() if err := rpcSvr.RegisterName(RPCPrefix, &RPC{visor: visor}); err != nil { return fmt.Errorf("rpc server created failed: %s", err) @@ -434,6 +436,19 @@ func (visor *Visor) Exec(command string) ([]byte, error) { return cmd.CombinedOutput() } +// App returns a single app state of given name. +func (visor *Visor) App(name string) (*AppState, bool) { + app, ok := visor.appsConf[name] + if !ok { + return nil, false + } + state := &AppState{app.App, app.AutoStart, app.Port, AppStatusStopped} + if visor.procManager.Exists(app.App) { + state.Status = AppStatusRunning + } + return state, true +} + // Apps returns list of AppStates for all registered apps. func (visor *Visor) Apps() []*AppState { // TODO: move app states to the app module diff --git a/vendor/github.com/SkycoinProject/dmsg/httputil/httputil.go b/vendor/github.com/SkycoinProject/dmsg/httputil/httputil.go index 53ce9ce2d0..e299a9c8fb 100644 --- a/vendor/github.com/SkycoinProject/dmsg/httputil/httputil.go +++ b/vendor/github.com/SkycoinProject/dmsg/httputil/httputil.go @@ -6,6 +6,8 @@ import ( "io" "net" "net/http" + "strconv" + "strings" "github.com/SkycoinProject/skycoin/src/util/logging" "github.com/gorilla/handlers" @@ -71,3 +73,14 @@ func WriteLog(writer io.Writer, params handlers.LogFormatterParams) { log.WithError(err).Warn("Failed to write log") } } + +// SplitRPCAddr returns host and port and whatever error results from parsing the rpc address interface +func SplitRPCAddr(rpcAddr string) (host string, port uint16, err error) { + addrToken := strings.Split(rpcAddr, ":") + uint64port, err := strconv.ParseUint(addrToken[1], 10, 16) + if err != nil { + return + } + + return addrToken[0], uint16(uint64port), nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 75fdd80e20..2692f7638a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,4 +1,4 @@ -# github.com/SkycoinProject/dmsg v0.0.0-20200220122410-79d9d7bac617 +# github.com/SkycoinProject/dmsg v0.0.0-20200224064625-1b539081519c github.com/SkycoinProject/dmsg github.com/SkycoinProject/dmsg/cipher github.com/SkycoinProject/dmsg/disc