From 4161c270d73f7ac706f9b2d2d8314378d2fc5609 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Mon, 11 Dec 2023 23:16:57 -0600 Subject: [PATCH 01/14] debugWeb 1 --- cmd/lotus-provider/rpc/rpc.go | 18 +- cmd/lotus-provider/web/api/debug/debug.go | 95 +++++++++ cmd/lotus-provider/web/api/routes.go | 12 ++ cmd/lotus-provider/web/srv.go | 40 ++++ cmd/lotus-provider/web/static/index.html | 181 ++++++++++++++++++ .../web/static/modules/chain-connectivity.js | 73 +++++++ node/config/def.go | 4 +- node/config/types.go | 3 + 8 files changed, 423 insertions(+), 3 deletions(-) create mode 100644 cmd/lotus-provider/web/api/debug/debug.go create mode 100644 cmd/lotus-provider/web/api/routes.go create mode 100644 cmd/lotus-provider/web/srv.go create mode 100644 cmd/lotus-provider/web/static/index.html create mode 100644 cmd/lotus-provider/web/static/modules/chain-connectivity.js diff --git a/cmd/lotus-provider/rpc/rpc.go b/cmd/lotus-provider/rpc/rpc.go index 1f075f79a34..4b4a77cf911 100644 --- a/cmd/lotus-provider/rpc/rpc.go +++ b/cmd/lotus-provider/rpc/rpc.go @@ -13,6 +13,7 @@ import ( "github.com/gorilla/mux" logging "github.com/ipfs/go-log/v2" "go.opencensus.io/tag" + "golang.org/x/sync/errgroup" "golang.org/x/xerrors" "github.com/filecoin-project/go-jsonrpc" @@ -20,6 +21,7 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" + "github.com/filecoin-project/lotus/cmd/lotus-provider/web" "github.com/filecoin-project/lotus/lib/rpcenc" "github.com/filecoin-project/lotus/metrics" "github.com/filecoin-project/lotus/metrics/proxy" @@ -126,15 +128,27 @@ func ListenAndServe(ctx context.Context, dependencies *deps.Deps, shutdownChan c Addr: dependencies.ListenAddr, } + log.Infof("Setting up RPC server at %s", dependencies.ListenAddr) + + web, err := web.GetSrv(ctx, dependencies) + if err != nil { + return err + } + go func() { <-ctx.Done() log.Warn("Shutting down...") if err := srv.Shutdown(context.TODO()); err != nil { log.Errorf("shutting down RPC server failed: %s", err) } + if err := web.Shutdown(context.Background()); err != nil { + log.Errorf("shutting down web server failed: %s", err) + } log.Warn("Graceful shutdown successful") }() - log.Infof("Setting up RPC server at %s", dependencies.ListenAddr) - return srv.ListenAndServe() + eg := errgroup.Group{} + eg.Go(srv.ListenAndServe) + eg.Go(web.ListenAndServe) + return eg.Wait() } diff --git a/cmd/lotus-provider/web/api/debug/debug.go b/cmd/lotus-provider/web/api/debug/debug.go new file mode 100644 index 00000000000..1dcd7c5a371 --- /dev/null +++ b/cmd/lotus-provider/web/api/debug/debug.go @@ -0,0 +1,95 @@ +// Package debug provides the API for various debug endpoints in lotus-provider. +package debug + +import ( + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/gorilla/mux" + logging "github.com/ipfs/go-log/v2" + + "github.com/filecoin-project/lotus/build" + cliutil "github.com/filecoin-project/lotus/cli/util" + "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" +) + +var log = logging.Logger("lp/web/debug") + +type debug struct { + *deps.Deps +} + +func Routes(r *mux.Router, deps *deps.Deps) { + d := debug{deps} + r.Methods("GET").Path("chain-state-sse").HandlerFunc(d.chainStateSSE) +} + +type rpcInfo struct { + Address string + CLayers []string + Reachable bool + SyncState string + Version string +} + +func (d *debug) chainStateSSE(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Headers", "Content-Type") + w.Header().Set("Content-Type", "text/event-stream") + w.Header().Set("Cache-Control", "no-cache") + w.Header().Set("Connection", "keep-alive") + + v1api := d.Deps.Full + ctx := r.Context() + + ai := cliutil.ParseApiInfo(d.Deps.Cfg.Apis.ChainApiInfo[0]) + ver, err := v1api.Version(ctx) + if err != nil { + log.Warnw("Version", "error", err) + return + } + +sse: + for { + head, err := v1api.ChainHead(ctx) + if err != nil { + log.Warnw("ChainHead", "error", err) + return + } + + var syncState string + switch { + case time.Now().Unix()-int64(head.MinTimestamp()) < int64(build.BlockDelaySecs*3/2): // within 1.5 epochs + syncState = "ok" + case time.Now().Unix()-int64(head.MinTimestamp()) < int64(build.BlockDelaySecs*5): // within 5 epochs + syncState = fmt.Sprintf("slow (%s behind)", time.Since(time.Unix(int64(head.MinTimestamp()), 0)).Truncate(time.Second)) + default: + syncState = fmt.Sprintf("behind (%s behind)", time.Since(time.Unix(int64(head.MinTimestamp()), 0)).Truncate(time.Second)) + } + + select { + case <-ctx.Done(): + break sse + default: + } + + fmt.Fprintf(w, "data: ") + err = json.NewEncoder(w).Encode(rpcInfo{ + Address: ai.Addr, + CLayers: []string{}, + Reachable: true, + Version: ver.Version, + SyncState: syncState, + }) + if err != nil { + log.Warnw("json encode", "error", err) + return + } + fmt.Fprintf(w, "\n\n") + if f, ok := w.(http.Flusher); ok { + f.Flush() + } + } +} diff --git a/cmd/lotus-provider/web/api/routes.go b/cmd/lotus-provider/web/api/routes.go new file mode 100644 index 00000000000..91e3177223a --- /dev/null +++ b/cmd/lotus-provider/web/api/routes.go @@ -0,0 +1,12 @@ +// Package api provides the HTTP API for the lotus provider web gui. +package api + +import ( + "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" + "github.com/filecoin-project/lotus/cmd/lotus-provider/web/api/debug" + "github.com/gorilla/mux" +) + +func Routes(r *mux.Router, deps *deps.Deps) { + debug.Routes(r.PathPrefix("/debug").Subrouter(), deps) +} diff --git a/cmd/lotus-provider/web/srv.go b/cmd/lotus-provider/web/srv.go new file mode 100644 index 00000000000..f59a8d83755 --- /dev/null +++ b/cmd/lotus-provider/web/srv.go @@ -0,0 +1,40 @@ +// Package web defines the HTTP web server for static files and endpoints. +package web + +import ( + "context" + "embed" + "net" + "net/http" + "strings" + + "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" + "github.com/filecoin-project/lotus/cmd/lotus-provider/web/api" + "github.com/filecoin-project/lotus/metrics" + "github.com/gorilla/mux" + "go.opencensus.io/tag" +) + +// go:embed static +var static embed.FS + +func GetSrv(ctx context.Context, deps *deps.Deps) (*http.Server, error) { + mux := mux.NewRouter() + api.Routes(mux.PathPrefix("/api").Subrouter(), deps) + mux.NotFoundHandler = http.FileServer(http.FS(static)) + + return &http.Server{ + Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if strings.HasSuffix(r.URL.Path, "/") { + r.URL.Path = r.URL.Path + "index.html" + return + } + mux.ServeHTTP(w, r) + }), + BaseContext: func(listener net.Listener) context.Context { + ctx, _ := tag.New(context.Background(), tag.Upsert(metrics.APIInterface, "lotus-provider")) + return ctx + }, + Addr: deps.Cfg.Subsystems.GuiAddress, + }, nil +} diff --git a/cmd/lotus-provider/web/static/index.html b/cmd/lotus-provider/web/static/index.html new file mode 100644 index 00000000000..731ee799b77 --- /dev/null +++ b/cmd/lotus-provider/web/static/index.html @@ -0,0 +1,181 @@ + + + + Lotus Provider Cluster Overview + + + + + +
+
+

Lotus Provider Cluster

+
+
+ version [todo] +
+
+
+
+
+

Chain Connectivity

+ +
+
+
+
+

Actor Summary

+ + + + + + + + + + + + + + + + + +
AddressConfig LayersQaPDeadlines
f01234mig023TiB +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + \ No newline at end of file diff --git a/cmd/lotus-provider/web/static/modules/chain-connectivity.js b/cmd/lotus-provider/web/static/modules/chain-connectivity.js new file mode 100644 index 00000000000..871f4009400 --- /dev/null +++ b/cmd/lotus-provider/web/static/modules/chain-connectivity.js @@ -0,0 +1,73 @@ +import { LitElement, html, css } from 'https://cdn.jsdelivr.net/npm/lit-html@3.1.0/lit-html.min.js'; +window.customElements.define('chain-connectivity', class MyElement extends LitElement { + constructor() { + super(); + this.data = []; + this.loadData(); + } + loadData() { + const eventSource = new EventSource('/api/debug/chain-state-sse'); + eventSource.onmessage = (event) => { + this.data.push(JSON.parse(event.data)); + }; + eventSource.onerror = (error) => { + console.error('Error:', error); + loadData(); + }; + }; + + static get styles() { + return [css` + :host { + box-sizing: border-box; /* Don't forgert this to include padding/border inside width calculation */ + } + table { + border-collapse: collapse; + } + + table td, table th { + border-left: 1px solid #f0f0f0; + padding: 1px 5px; + } + + table tr td:first-child, table tr th:first-child { + border-left: none; + } + + .success { + color: green; + } + .warning { + color: yellow; + } + .error { + color: red; + } + `]; + } + render = () => html` + + + + + + + + + + + ${this.data.map(item => html` + + + + + + + + `)} + + + + +
RPC AddressReachabilitySync StatusVersion
{{.Address}}${item.Address}${item.Reachable ? html`ok` : html`FAIL`}${item.SyncState === "ok" ? html`ok` : html`${item.SyncState}`}${item.Version}
Data incoming...
` +}); diff --git a/node/config/def.go b/node/config/def.go index dc358b14017..e8f315add5a 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -351,7 +351,9 @@ func DefaultUserRaftConfig() *UserRaftConfig { func DefaultLotusProvider() *LotusProviderConfig { return &LotusProviderConfig{ - Subsystems: ProviderSubsystemsConfig{}, + Subsystems: ProviderSubsystemsConfig{ + GuiAddress: ":4701", + }, Fees: LotusProviderFees{ DefaultMaxFee: DefaultDefaultMaxFee, MaxPreCommitGasFee: types.MustParseFIL("0.025"), diff --git a/node/config/types.go b/node/config/types.go index 2152e079569..6233191f41c 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -96,6 +96,9 @@ type ProviderSubsystemsConfig struct { WindowPostMaxTasks int EnableWinningPost bool WinningPostMaxTasks int + + // The address that should listen for Web GUI requests. + GuiAddress string } type DAGStoreConfig struct { From 17e17eaac3fe6542a26ac2ac16749b8f807c15fd Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Mon, 11 Dec 2023 23:36:04 -0600 Subject: [PATCH 02/14] move --- cmd/lotus-provider/web/static/{modules => }/chain-connectivity.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cmd/lotus-provider/web/static/{modules => }/chain-connectivity.js (100%) diff --git a/cmd/lotus-provider/web/static/modules/chain-connectivity.js b/cmd/lotus-provider/web/static/chain-connectivity.js similarity index 100% rename from cmd/lotus-provider/web/static/modules/chain-connectivity.js rename to cmd/lotus-provider/web/static/chain-connectivity.js From 8070e3424f8f5a761f05eec2c92ddbe642c5bdac Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Mon, 11 Dec 2023 23:47:31 -0600 Subject: [PATCH 03/14] fix deps --- cmd/lotus-provider/web/static/chain-connectivity.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/lotus-provider/web/static/chain-connectivity.js b/cmd/lotus-provider/web/static/chain-connectivity.js index 871f4009400..2381a2f22ee 100644 --- a/cmd/lotus-provider/web/static/chain-connectivity.js +++ b/cmd/lotus-provider/web/static/chain-connectivity.js @@ -1,4 +1,4 @@ -import { LitElement, html, css } from 'https://cdn.jsdelivr.net/npm/lit-html@3.1.0/lit-html.min.js'; +import { LitElement, html, css } from 'https://cdn.jsdelivr.net/gh/lit/dist@3/core/lit-all.min.js'; window.customElements.define('chain-connectivity', class MyElement extends LitElement { constructor() { super(); From 5717ec8c0af286e1d3832ace7c561ca2bbdf7f07 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Mon, 11 Dec 2023 23:48:07 -0600 Subject: [PATCH 04/14] fix deps2 --- cmd/lotus-provider/web/static/chain-connectivity.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/lotus-provider/web/static/chain-connectivity.js b/cmd/lotus-provider/web/static/chain-connectivity.js index 2381a2f22ee..6fc63234fe4 100644 --- a/cmd/lotus-provider/web/static/chain-connectivity.js +++ b/cmd/lotus-provider/web/static/chain-connectivity.js @@ -1,4 +1,4 @@ -import { LitElement, html, css } from 'https://cdn.jsdelivr.net/gh/lit/dist@3/core/lit-all.min.js'; +import { LitElement, html, css } from 'https://cdn.jsdelivr.net/gh/lit/dist@3/all/lit-all.min.js'; window.customElements.define('chain-connectivity', class MyElement extends LitElement { constructor() { super(); From bc76004c1564da48ecda173b92d65c038e50e30a Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Tue, 12 Dec 2023 08:07:38 -0600 Subject: [PATCH 05/14] module --- cmd/lotus-provider/web/static/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/lotus-provider/web/static/index.html b/cmd/lotus-provider/web/static/index.html index 731ee799b77..00b48b6fddd 100644 --- a/cmd/lotus-provider/web/static/index.html +++ b/cmd/lotus-provider/web/static/index.html @@ -2,7 +2,7 @@ Lotus Provider Cluster Overview - + - -
-
-

Lotus Provider Cluster

-
-
- version [todo] -
+
+
+

Lotus Provider Cluster

+
+
+ version [todo]
-
-
-
-

Chain Connectivity

- -
+
+
+
+
+

Chain Connectivity

+

Actor Summary

- - - - - - + + + + + + + + + +
AddressConfig LayersQaPDeadlines
AddressConfig LayersQaPDeadlines
+
+
+
+

Cluster Machines

+ + + + + + + + + + + +
HostIDConfig LayersLast Contact
+
+
+
+

Recently Finished Tasks

+ + + + + + + + + + + + - - - - - - - +
NameIDExecutorPostedStartEndOutcomeMessage
f01234mig023TiB -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+

Cluster Tasks

+ + + + + + + + + + + +
TaskIDPostedOwner
+
- \ No newline at end of file diff --git a/lib/harmony/harmonytask/task_type_handler.go b/lib/harmony/harmonytask/task_type_handler.go index ccfcc1f6fdf..a6e8933d21d 100644 --- a/lib/harmony/harmonytask/task_type_handler.go +++ b/lib/harmony/harmonytask/task_type_handler.go @@ -184,6 +184,9 @@ retryRecordCompletion: return false, fmt.Errorf("could not log completion: %w", err) } result = "" + if doErr != nil { + result = "non-failing error: " + doErr.Error() + } } else { if doErr != nil { result = "error: " + doErr.Error() diff --git a/provider/lpwinning/winning_task.go b/provider/lpwinning/winning_task.go index 907b594fd01..6e5e1cc5ab3 100644 --- a/provider/lpwinning/winning_task.go +++ b/provider/lpwinning/winning_task.go @@ -156,13 +156,18 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don ComputeTime: details.CompTime, } - persistNoWin := func() error { - _, err := t.db.Exec(ctx, `UPDATE mining_base_block SET no_win = true WHERE task_id = $1`, taskID) + persistNoWin := func() (bool, error) { + n, err := t.db.Exec(ctx, `UPDATE mining_base_block SET no_win = true WHERE task_id = $1`, taskID) if err != nil { - return xerrors.Errorf("marking base as not-won: %w", err) + return false, xerrors.Errorf("marking base as not-won: %w", err) } + log.Debugw("persisted no-win", "rows", n) - return nil + if n == 0 { + return false, xerrors.Errorf("persist no win: no rows updated") + } + + return false, nil } // ensure we have a beacon entry for the epoch we're mining on @@ -182,13 +187,13 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don if mbi == nil { // not eligible to mine on this base, we're done here log.Debugw("WinPoSt not eligible to mine on this base", "tipset", types.LogCids(base.TipSet.Cids())) - return true, persistNoWin() + return persistNoWin() } if !mbi.EligibleForMining { // slashed or just have no power yet, we're done here log.Debugw("WinPoSt not eligible for mining", "tipset", types.LogCids(base.TipSet.Cids())) - return true, persistNoWin() + return persistNoWin() } if len(mbi.Sectors) == 0 { @@ -217,7 +222,7 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don if eproof == nil { // not a winner, we're done here log.Debugw("WinPoSt not a winner", "tipset", types.LogCids(base.TipSet.Cids())) - return true, persistNoWin() + return persistNoWin() } } From 25b228c2f60cf9c0021544f06d11d766e64dc01a Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Tue, 19 Dec 2023 09:12:45 -0600 Subject: [PATCH 09/14] fix: lint --- cmd/lotus-provider/web/api/routes.go | 3 ++- cmd/lotus-provider/web/hapi/routes.go | 4 ++-- cmd/lotus-provider/web/srv.go | 5 +++-- documentation/en/cli-lotus-provider.md | 18 ++++++++++++++++++ .../en/default-lotus-provider-config.toml | 8 ++++++++ node/config/doc_gen.go | 12 ++++++++++++ 6 files changed, 45 insertions(+), 5 deletions(-) diff --git a/cmd/lotus-provider/web/api/routes.go b/cmd/lotus-provider/web/api/routes.go index 91e3177223a..9bb6fb67c07 100644 --- a/cmd/lotus-provider/web/api/routes.go +++ b/cmd/lotus-provider/web/api/routes.go @@ -2,9 +2,10 @@ package api import ( + "github.com/gorilla/mux" + "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" "github.com/filecoin-project/lotus/cmd/lotus-provider/web/api/debug" - "github.com/gorilla/mux" ) func Routes(r *mux.Router, deps *deps.Deps) { diff --git a/cmd/lotus-provider/web/hapi/routes.go b/cmd/lotus-provider/web/hapi/routes.go index b56c8c7c0d0..d327a76a854 100644 --- a/cmd/lotus-provider/web/hapi/routes.go +++ b/cmd/lotus-provider/web/hapi/routes.go @@ -4,11 +4,11 @@ import ( "embed" "html/template" + "github.com/gorilla/mux" logging "github.com/ipfs/go-log/v2" + "golang.org/x/xerrors" "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" - "github.com/gorilla/mux" - "golang.org/x/xerrors" ) //go:embed web/* diff --git a/cmd/lotus-provider/web/srv.go b/cmd/lotus-provider/web/srv.go index 8692800056d..f41501fefcd 100644 --- a/cmd/lotus-provider/web/srv.go +++ b/cmd/lotus-provider/web/srv.go @@ -8,12 +8,13 @@ import ( "net/http" "strings" + "github.com/gorilla/mux" + "go.opencensus.io/tag" + "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" "github.com/filecoin-project/lotus/cmd/lotus-provider/web/api" "github.com/filecoin-project/lotus/cmd/lotus-provider/web/hapi" "github.com/filecoin-project/lotus/metrics" - "github.com/gorilla/mux" - "go.opencensus.io/tag" ) // go:embed static diff --git a/documentation/en/cli-lotus-provider.md b/documentation/en/cli-lotus-provider.md index de77697e351..a01cd0856ef 100644 --- a/documentation/en/cli-lotus-provider.md +++ b/documentation/en/cli-lotus-provider.md @@ -14,6 +14,7 @@ COMMANDS: stop Stop a running lotus provider config Manage node config by layers. The layer 'base' will always be applied. test Utility functions for testing + web Start lotus provider web interface version Print version help, h Shows a list of commands or help for one command DEVELOPER: @@ -247,6 +248,23 @@ OPTIONS: --help, -h show help ``` +## lotus-provider web +``` +NAME: + lotus-provider web - Start lotus provider web interface + +USAGE: + lotus-provider web [command options] [arguments...] + +DESCRIPTION: + Start an instance of lotus provider web interface. + This creates the 'web' layer if it does not exist, then calls run with that layer. + +OPTIONS: + --listen value Address to listen on (default: "127.0.0.1:4701") + --help, -h show help +``` + ## lotus-provider version ``` NAME: diff --git a/documentation/en/default-lotus-provider-config.toml b/documentation/en/default-lotus-provider-config.toml index 91606e503fd..8573fbda17a 100644 --- a/documentation/en/default-lotus-provider-config.toml +++ b/documentation/en/default-lotus-provider-config.toml @@ -11,6 +11,14 @@ # type: int #WinningPostMaxTasks = 0 + # type: bool + #EnableWebGui = false + + # The address that should listen for Web GUI requests. + # + # type: string + #GuiAddress = ":4701" + [Fees] # type: types.FIL diff --git a/node/config/doc_gen.go b/node/config/doc_gen.go index 13ecb2706e3..012214770c1 100644 --- a/node/config/doc_gen.go +++ b/node/config/doc_gen.go @@ -1013,6 +1013,18 @@ block rewards will be missed!`, Comment: ``, }, + { + Name: "EnableWebGui", + Type: "bool", + + Comment: ``, + }, + { + Name: "GuiAddress", + Type: "string", + + Comment: `The address that should listen for Web GUI requests.`, + }, }, "ProvingConfig": { { From 8dffab5d3964487448f232ab4ff7a3fe46283598 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Tue, 19 Dec 2023 18:58:55 -0600 Subject: [PATCH 10/14] fix: lp lint and dev simplicities --- cmd/lotus-provider/run.go | 8 ++++++-- cmd/lotus-provider/web/hapi/simpleinfo.go | 2 +- cmd/lotus-provider/web/srv.go | 13 ++++++++++++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/cmd/lotus-provider/run.go b/cmd/lotus-provider/run.go index 00e3079d965..a47cb6ec42a 100644 --- a/cmd/lotus-provider/run.go +++ b/cmd/lotus-provider/run.go @@ -167,12 +167,16 @@ var webCmd = &cli.Command{ cfg := config.DefaultLotusProvider() cfg.Subsystems.EnableWebGui = true var b bytes.Buffer - toml.NewEncoder(&b).Encode(cfg) + if err = toml.NewEncoder(&b).Encode(cfg); err != nil { + return err + } if err = setConfig(db, "web", b.String()); err != nil { return err } } - cctx.Set("layers", "web") + if err = cctx.Set("layers", "web"); err != nil { + return err + } return runCmd.Action(cctx) }, } diff --git a/cmd/lotus-provider/web/hapi/simpleinfo.go b/cmd/lotus-provider/web/hapi/simpleinfo.go index 47073951878..a14735a84b8 100644 --- a/cmd/lotus-provider/web/hapi/simpleinfo.go +++ b/cmd/lotus-provider/web/hapi/simpleinfo.go @@ -77,7 +77,7 @@ func (a *app) indexTasksHistory(w http.ResponseWriter, r *http.Request) { a.executeTemplate(w, "cluster_task_history", s) } -var templateDev = os.Getenv("LOTUS_HAPI_TEMPLATE_DEV") == "1" +var templateDev = os.Getenv("LOTUS_WEB_DEV") == "1" func (a *app) executeTemplate(w http.ResponseWriter, name string, data interface{}) { if templateDev { diff --git a/cmd/lotus-provider/web/srv.go b/cmd/lotus-provider/web/srv.go index f41501fefcd..c618d0baac9 100644 --- a/cmd/lotus-provider/web/srv.go +++ b/cmd/lotus-provider/web/srv.go @@ -6,7 +6,9 @@ import ( "embed" "net" "net/http" + "os" "strings" + "time" "github.com/gorilla/mux" "go.opencensus.io/tag" @@ -20,6 +22,10 @@ import ( // go:embed static var static embed.FS +// An dev mode hack for no-restart changes to static and templates. +// You still need to recomplie the binary for changes to go code. +var webDev = os.Getenv("LOTUS_WEB_DEV") == "1" + func GetSrv(ctx context.Context, deps *deps.Deps) (*http.Server, error) { mux := mux.NewRouter() api.Routes(mux.PathPrefix("/api").Subrouter(), deps) @@ -28,6 +34,9 @@ func GetSrv(ctx context.Context, deps *deps.Deps) (*http.Server, error) { return nil, err } mux.NotFoundHandler = http.FileServer(http.FS(static)) + if webDev { + mux.NotFoundHandler = http.FileServer(http.Dir("cmd/lotus-provider/web/static")) + } return &http.Server{ Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -41,6 +50,8 @@ func GetSrv(ctx context.Context, deps *deps.Deps) (*http.Server, error) { ctx, _ := tag.New(context.Background(), tag.Upsert(metrics.APIInterface, "lotus-provider")) return ctx }, - Addr: deps.Cfg.Subsystems.GuiAddress, + Addr: deps.Cfg.Subsystems.GuiAddress, + ReadTimeout: time.Minute * 3, + ReadHeaderTimeout: time.Minute * 3, // lint }, nil } From 464e492fd20ac28da2030a2c5df66fb6d964038e Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Sat, 30 Dec 2023 09:37:04 -0600 Subject: [PATCH 11/14] srv cleanups --- cli/util/api.go | 2 +- cmd/lotus-provider/deps/deps.go | 8 +++- cmd/lotus-provider/run.go | 14 +++++- cmd/lotus-provider/web/api/debug/debug.go | 31 ++++++------- cmd/lotus-provider/web/hapi/routes.go | 4 +- cmd/lotus-provider/web/srv.go | 53 +++++++++++++++++------ 6 files changed, 75 insertions(+), 37 deletions(-) diff --git a/cli/util/api.go b/cli/util/api.go index 3602b752de2..fe1ac153678 100644 --- a/cli/util/api.go +++ b/cli/util/api.go @@ -445,7 +445,7 @@ func GetFullNodeAPIV1LotusProvider(ctx *cli.Context, ainfoCfg []string, opts ... for _, head := range heads { v1api, closer, err := client.NewFullNodeRPCV1(ctx.Context, head.addr, head.header, rpcOpts...) if err != nil { - log.Warnf("Not able to establish connection to node with addr: %s", head.addr) + log.Warnf("Not able to establish connection to node with addr: %s, Reason: %s", head.addr, err.Error()) continue } fullNodes = append(fullNodes, v1api) diff --git a/cmd/lotus-provider/deps/deps.go b/cmd/lotus-provider/deps/deps.go index 499cc7bec06..7a8db855f97 100644 --- a/cmd/lotus-provider/deps/deps.go +++ b/cmd/lotus-provider/deps/deps.go @@ -9,6 +9,7 @@ import ( "fmt" "net" "net/http" + "os" "strings" "github.com/BurntSushi/toml" @@ -176,7 +177,11 @@ func (deps *Deps) PopulateRemainingDeps(ctx context.Context, cctx *cli.Context, if deps.Full == nil { var fullCloser func() - deps.Full, fullCloser, err = cliutil.GetFullNodeAPIV1LotusProvider(cctx, deps.Cfg.Apis.ChainApiInfo) + cfgApiInfo := deps.Cfg.Apis.ChainApiInfo + if v := os.Getenv("FULLNODE_API_INFO"); v != "" { + cfgApiInfo = []string{v} + } + deps.Full, fullCloser, err = cliutil.GetFullNodeAPIV1LotusProvider(cctx, cfgApiInfo) if err != nil { return err } @@ -267,6 +272,7 @@ func GetConfig(cctx *cli.Context, db *harmonydb.DB) (*config.LotusProviderConfig for _, k := range meta.Keys() { have = append(have, strings.Join(k, " ")) } + log.Infow("Using layer", "layer", layer, "config", lp) } _ = have // FUTURE: verify that required fields are here. // If config includes 3rd-party config, consider JSONSchema as a way that diff --git a/cmd/lotus-provider/run.go b/cmd/lotus-provider/run.go index a47cb6ec42a..0f18f2843bd 100644 --- a/cmd/lotus-provider/run.go +++ b/cmd/lotus-provider/run.go @@ -5,6 +5,7 @@ import ( "context" "fmt" "os" + "strings" "time" "github.com/BurntSushi/toml" @@ -155,6 +156,15 @@ var webCmd = &cli.Command{ Usage: "Address to listen on", Value: "127.0.0.1:4701", }, + &cli.StringSliceFlag{ + Name: "layers", + Usage: "list of layers to be interpreted (atop defaults). Default: base. Web will be added", + Value: cli.NewStringSlice("base"), + }, + &cli.BoolFlag{ + Name: "nosync", + Usage: "don't check full-node sync status", + }, }, Action: func(cctx *cli.Context) error { db, err := deps.MakeDB(cctx) @@ -174,7 +184,9 @@ var webCmd = &cli.Command{ return err } } - if err = cctx.Set("layers", "web"); err != nil { + layers := append([]string{"web"}, cctx.StringSlice("layers")...) + err = cctx.Set("layers", strings.Join(layers, ",")) + if err != nil { return err } return runCmd.Action(cctx) diff --git a/cmd/lotus-provider/web/api/debug/debug.go b/cmd/lotus-provider/web/api/debug/debug.go index 7382ca3f81b..84568451970 100644 --- a/cmd/lotus-provider/web/api/debug/debug.go +++ b/cmd/lotus-provider/web/api/debug/debug.go @@ -29,7 +29,7 @@ type debug struct { func Routes(r *mux.Router, deps *deps.Deps) { d := debug{deps} - r.Methods("GET").Path("chain-state-sse").HandlerFunc(d.chainStateSSE) + r.HandleFunc("/chain-state-sse", d.chainStateSSE) } type rpcInfo struct { @@ -79,29 +79,24 @@ func (d *debug) chainStateSSE(w http.ResponseWriter, r *http.Request) { return } - apiInfos := map[string][]byte{} // api address -> token - // for dedup by address - for _, info := range rpcInfos { - ai := cliutil.ParseApiInfo(info.Apis.ChainApiInfo[0]) - apiInfos[ai.Addr] = ai.Token - } + dedup := map[string]bool{} // for dedup by address infos := map[string]rpcInfo{} // api address -> rpc info var infosLk sync.Mutex var wg sync.WaitGroup - wg.Add(len(rpcInfos)) - for addr, token := range apiInfos { - addr := addr - ai := cliutil.APIInfo{ - Addr: addr, - Token: token, + for _, info := range rpcInfos { + ai := cliutil.ParseApiInfo(info.Apis.ChainApiInfo[0]) + if dedup[ai.Addr] { + continue } - go func(info string) { + dedup[ai.Addr] = true + wg.Add(1) + go func() { defer wg.Done() var clayers []string for layer, a := range confNameToAddr { - if a == addr { + if a == ai.Addr { clayers = append(clayers, layer) } } @@ -113,8 +108,8 @@ func (d *debug) chainStateSSE(w http.ResponseWriter, r *http.Request) { } defer func() { infosLk.Lock() + defer infosLk.Unlock() infos[ai.Addr] = myinfo - infosLk.Unlock() }() da, err := ai.DialArgs("v1") if err != nil { @@ -126,7 +121,7 @@ func (d *debug) chainStateSSE(w http.ResponseWriter, r *http.Request) { v1api, closer, err := client.NewFullNodeRPCV1(ctx, da, ah) if err != nil { - log.Warnf("Not able to establish connection to node with addr: %s", addr) + log.Warnf("Not able to establish connection to node with addr: %s", ai.Addr) return } defer closer() @@ -160,7 +155,7 @@ func (d *debug) chainStateSSE(w http.ResponseWriter, r *http.Request) { Version: ver.Version, SyncState: syncState, } - }(addr) + }() } wg.Wait() diff --git a/cmd/lotus-provider/web/hapi/routes.go b/cmd/lotus-provider/web/hapi/routes.go index d327a76a854..b07ab60a57a 100644 --- a/cmd/lotus-provider/web/hapi/routes.go +++ b/cmd/lotus-provider/web/hapi/routes.go @@ -15,9 +15,7 @@ import ( var templateFS embed.FS func Routes(r *mux.Router, deps *deps.Deps) error { - - t := new(template.Template) - t, err := t.ParseFS(templateFS, "web/*") + t, err := template.ParseFS(templateFS, "web/*") if err != nil { return xerrors.Errorf("parse templates: %w", err) } diff --git a/cmd/lotus-provider/web/srv.go b/cmd/lotus-provider/web/srv.go index c618d0baac9..d409eb86812 100644 --- a/cmd/lotus-provider/web/srv.go +++ b/cmd/lotus-provider/web/srv.go @@ -4,9 +4,12 @@ package web import ( "context" "embed" + "io" + "io/fs" "net" "net/http" "os" + "path" "strings" "time" @@ -19,33 +22,57 @@ import ( "github.com/filecoin-project/lotus/metrics" ) -// go:embed static +//go:embed static var static embed.FS +var basePath = "/static/" + // An dev mode hack for no-restart changes to static and templates. // You still need to recomplie the binary for changes to go code. var webDev = os.Getenv("LOTUS_WEB_DEV") == "1" func GetSrv(ctx context.Context, deps *deps.Deps) (*http.Server, error) { - mux := mux.NewRouter() - api.Routes(mux.PathPrefix("/api").Subrouter(), deps) - err := hapi.Routes(mux.PathPrefix("/hapi").Subrouter(), deps) + mx := mux.NewRouter() + err := hapi.Routes(mx.PathPrefix("/hapi").Subrouter(), deps) if err != nil { return nil, err } - mux.NotFoundHandler = http.FileServer(http.FS(static)) + api.Routes(mx.PathPrefix("/api").Subrouter(), deps) + + basePath := basePath + + var static fs.FS = static if webDev { - mux.NotFoundHandler = http.FileServer(http.Dir("cmd/lotus-provider/web/static")) + basePath = "cmd/lotus-provider/web/static" + static = os.DirFS(basePath) } + mx.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // If the request is for a directory, redirect to the index file. + if strings.HasSuffix(r.URL.Path, "/") { + r.URL.Path += "index.html" + } + + file, err := static.Open(path.Join(basePath, r.URL.Path)[1:]) + if err != nil { + w.WriteHeader(http.StatusNotFound) + w.Write([]byte("404 Not Found")) + return + } + defer file.Close() + + fileInfo, err := file.Stat() + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte("500 Internal Server Error")) + return + } + + http.ServeContent(w, r, fileInfo.Name(), fileInfo.ModTime(), file.(io.ReadSeeker)) + }) + return &http.Server{ - Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if strings.HasSuffix(r.URL.Path, "/") { - r.URL.Path = r.URL.Path + "index.html" - return - } - mux.ServeHTTP(w, r) - }), + Handler: http.HandlerFunc(mx.ServeHTTP), BaseContext: func(listener net.Listener) context.Context { ctx, _ := tag.New(context.Background(), tag.Upsert(metrics.APIInterface, "lotus-provider")) return ctx From 56bbeb01af7d55489ea69f83847d39a47ba087bb Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Tue, 2 Jan 2024 11:29:55 -0600 Subject: [PATCH 12/14] fix: lint --- cmd/lotus-provider/web/srv.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/lotus-provider/web/srv.go b/cmd/lotus-provider/web/srv.go index d409eb86812..55d20cc9ad0 100644 --- a/cmd/lotus-provider/web/srv.go +++ b/cmd/lotus-provider/web/srv.go @@ -56,15 +56,15 @@ func GetSrv(ctx context.Context, deps *deps.Deps) (*http.Server, error) { file, err := static.Open(path.Join(basePath, r.URL.Path)[1:]) if err != nil { w.WriteHeader(http.StatusNotFound) - w.Write([]byte("404 Not Found")) + _, _ = w.Write([]byte("404 Not Found")) return } - defer file.Close() + defer func() { _ = file.Close() }() fileInfo, err := file.Stat() if err != nil { w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte("500 Internal Server Error")) + _, _ = w.Write([]byte("500 Internal Server Error")) return } From ad8e90d31cfecf14daa09b603409734280bc3e29 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Wed, 3 Jan 2024 16:18:23 -0600 Subject: [PATCH 13/14] lp debugWeb ui bug --- cmd/lotus-provider/web/static/chain-connectivity.js | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/lotus-provider/web/static/chain-connectivity.js b/cmd/lotus-provider/web/static/chain-connectivity.js index 1b37666bb04..ea7349c41cb 100644 --- a/cmd/lotus-provider/web/static/chain-connectivity.js +++ b/cmd/lotus-provider/web/static/chain-connectivity.js @@ -59,7 +59,6 @@ window.customElements.define('chain-connectivity', class MyElement extends LitEl ${this.data.map(item => html` - {{.Address}} ${item.Address} ${item.Reachable ? html`ok` : html`FAIL`} ${item.SyncState === "ok" ? html`ok` : html`${item.SyncState}`} From fc8e4c54f8479e15e5fed784c1db18a525a5cf30 Mon Sep 17 00:00:00 2001 From: "Andrew Jackson (Ajax)" Date: Thu, 4 Jan 2024 09:47:56 -0600 Subject: [PATCH 14/14] docsgen --- documentation/en/cli-lotus-provider.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/documentation/en/cli-lotus-provider.md b/documentation/en/cli-lotus-provider.md index a01cd0856ef..5e58641075e 100644 --- a/documentation/en/cli-lotus-provider.md +++ b/documentation/en/cli-lotus-provider.md @@ -261,8 +261,10 @@ DESCRIPTION: This creates the 'web' layer if it does not exist, then calls run with that layer. OPTIONS: - --listen value Address to listen on (default: "127.0.0.1:4701") - --help, -h show help + --listen value Address to listen on (default: "127.0.0.1:4701") + --layers value [ --layers value ] list of layers to be interpreted (atop defaults). Default: base. Web will be added (default: "base") + --nosync don't check full-node sync status (default: false) + --help, -h show help ``` ## lotus-provider version