debugWeb 1
This commit is contained in:
parent
a478b734fd
commit
4161c270d7
@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
logging "github.com/ipfs/go-log/v2"
|
logging "github.com/ipfs/go-log/v2"
|
||||||
"go.opencensus.io/tag"
|
"go.opencensus.io/tag"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-jsonrpc"
|
"github.com/filecoin-project/go-jsonrpc"
|
||||||
@ -20,6 +21,7 @@ import (
|
|||||||
|
|
||||||
"github.com/filecoin-project/lotus/api"
|
"github.com/filecoin-project/lotus/api"
|
||||||
"github.com/filecoin-project/lotus/cmd/lotus-provider/deps"
|
"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/lib/rpcenc"
|
||||||
"github.com/filecoin-project/lotus/metrics"
|
"github.com/filecoin-project/lotus/metrics"
|
||||||
"github.com/filecoin-project/lotus/metrics/proxy"
|
"github.com/filecoin-project/lotus/metrics/proxy"
|
||||||
@ -126,15 +128,27 @@ func ListenAndServe(ctx context.Context, dependencies *deps.Deps, shutdownChan c
|
|||||||
Addr: dependencies.ListenAddr,
|
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() {
|
go func() {
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
log.Warn("Shutting down...")
|
log.Warn("Shutting down...")
|
||||||
if err := srv.Shutdown(context.TODO()); err != nil {
|
if err := srv.Shutdown(context.TODO()); err != nil {
|
||||||
log.Errorf("shutting down RPC server failed: %s", err)
|
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.Warn("Graceful shutdown successful")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
log.Infof("Setting up RPC server at %s", dependencies.ListenAddr)
|
eg := errgroup.Group{}
|
||||||
return srv.ListenAndServe()
|
eg.Go(srv.ListenAndServe)
|
||||||
|
eg.Go(web.ListenAndServe)
|
||||||
|
return eg.Wait()
|
||||||
}
|
}
|
||||||
|
95
cmd/lotus-provider/web/api/debug/debug.go
Normal file
95
cmd/lotus-provider/web/api/debug/debug.go
Normal file
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
cmd/lotus-provider/web/api/routes.go
Normal file
12
cmd/lotus-provider/web/api/routes.go
Normal file
@ -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)
|
||||||
|
}
|
40
cmd/lotus-provider/web/srv.go
Normal file
40
cmd/lotus-provider/web/srv.go
Normal file
@ -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
|
||||||
|
}
|
181
cmd/lotus-provider/web/static/index.html
Normal file
181
cmd/lotus-provider/web/static/index.html
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Lotus Provider Cluster Overview</title>
|
||||||
|
<script module src="chain-connectivity.js"></script>
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
background: #0f0f0f;
|
||||||
|
color: #ffffff;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-head {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.head-left {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.head-right {
|
||||||
|
display: inline-block;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
a:link {
|
||||||
|
color: #cfc;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:visited {
|
||||||
|
color: #dfa;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: #af7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-tile {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0.75rem;
|
||||||
|
background: #3f3f3f;
|
||||||
|
|
||||||
|
& b {
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
color: deeppink;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.deadline-box {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(16, auto);
|
||||||
|
grid-template-rows: repeat(3, auto);
|
||||||
|
grid-gap: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deadline-entry {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
background-color: grey;
|
||||||
|
margin: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deadline-entry-cur {
|
||||||
|
border-bottom: 3px solid deepskyblue;
|
||||||
|
height: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deadline-proven {
|
||||||
|
background-color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deadline-partially-faulty {
|
||||||
|
background-color: yellow;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deadline-faulty {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="app-head">
|
||||||
|
<div class="head-left">
|
||||||
|
<h1>Lotus Provider Cluster</h1>
|
||||||
|
</div>
|
||||||
|
<div class="head-right">
|
||||||
|
version [todo]
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="page">
|
||||||
|
<div class="info-block">
|
||||||
|
<h2>Chain Connectivity</h2>
|
||||||
|
<chain-connectivity></chain-connectivity>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="info-block">
|
||||||
|
<h2>Actor Summary</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Address</th>
|
||||||
|
<th>Config Layers</th>
|
||||||
|
<th>QaP</th>
|
||||||
|
<th>Deadlines</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>f01234</td>
|
||||||
|
<td>mig0</td>
|
||||||
|
<td>23TiB</td>
|
||||||
|
<td>
|
||||||
|
<div class="deadline-box">
|
||||||
|
<div class="deadline-entry deadline-proven"></div>
|
||||||
|
<div class="deadline-entry deadline-partially-faulty"></div>
|
||||||
|
<div class="deadline-entry deadline-faulty"></div>
|
||||||
|
<div class="deadline-entry deadline-proven"></div>
|
||||||
|
<div class="deadline-entry deadline-proven"></div>
|
||||||
|
<div class="deadline-entry deadline-entry-cur"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
<div class="deadline-entry"></div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
73
cmd/lotus-provider/web/static/modules/chain-connectivity.js
Normal file
73
cmd/lotus-provider/web/static/modules/chain-connectivity.js
Normal file
@ -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`
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>RPC Address</th>
|
||||||
|
<th>Reachability</th>
|
||||||
|
<th>Sync Status</th>
|
||||||
|
<th>Version</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
${this.data.map(item => html`
|
||||||
|
<tr>
|
||||||
|
<td>{{.Address}}</td>
|
||||||
|
<td>${item.Address}</td>
|
||||||
|
<td>${item.Reachable ? html`<span class="success">ok</span>` : html`<span class="error">FAIL</span>`}</td>
|
||||||
|
<td>${item.SyncState === "ok" ? html`<span class="success">ok</span>` : html`<span class="warning">${item.SyncState}</span>`}</td>
|
||||||
|
<td>${item.Version}</td>
|
||||||
|
</tr>
|
||||||
|
`)}
|
||||||
|
<tr>
|
||||||
|
<td colspan="4">Data incoming...</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>`
|
||||||
|
});
|
@ -351,7 +351,9 @@ func DefaultUserRaftConfig() *UserRaftConfig {
|
|||||||
|
|
||||||
func DefaultLotusProvider() *LotusProviderConfig {
|
func DefaultLotusProvider() *LotusProviderConfig {
|
||||||
return &LotusProviderConfig{
|
return &LotusProviderConfig{
|
||||||
Subsystems: ProviderSubsystemsConfig{},
|
Subsystems: ProviderSubsystemsConfig{
|
||||||
|
GuiAddress: ":4701",
|
||||||
|
},
|
||||||
Fees: LotusProviderFees{
|
Fees: LotusProviderFees{
|
||||||
DefaultMaxFee: DefaultDefaultMaxFee,
|
DefaultMaxFee: DefaultDefaultMaxFee,
|
||||||
MaxPreCommitGasFee: types.MustParseFIL("0.025"),
|
MaxPreCommitGasFee: types.MustParseFIL("0.025"),
|
||||||
|
@ -96,6 +96,9 @@ type ProviderSubsystemsConfig struct {
|
|||||||
WindowPostMaxTasks int
|
WindowPostMaxTasks int
|
||||||
EnableWinningPost bool
|
EnableWinningPost bool
|
||||||
WinningPostMaxTasks int
|
WinningPostMaxTasks int
|
||||||
|
|
||||||
|
// The address that should listen for Web GUI requests.
|
||||||
|
GuiAddress string
|
||||||
}
|
}
|
||||||
|
|
||||||
type DAGStoreConfig struct {
|
type DAGStoreConfig struct {
|
||||||
|
Loading…
Reference in New Issue
Block a user