squash of 11515 with some fixups - not tested

This commit is contained in:
Andrew Jackson (Ajax) 2023-12-18 19:23:40 -06:00
parent 223e08bb84
commit b12757485a
13 changed files with 414 additions and 98 deletions

View File

@ -164,7 +164,7 @@ func (deps *Deps) PopulateRemainingDeps(ctx context.Context, cctx *cli.Context,
if err != nil { if err != nil {
return err return err
} }
defer func() { go func() {
<-ctx.Done() <-ctx.Done()
_ = j.Close() _ = j.Close()
}() }()

View File

@ -116,10 +116,8 @@ var runCmd = &cli.Command{
} }
} }
fmt.Println("before populateRemainingDeps")
dependencies := &deps.Deps{} dependencies := &deps.Deps{}
err = dependencies.PopulateRemainingDeps(ctx, cctx, true) err = dependencies.PopulateRemainingDeps(ctx, cctx, true)
fmt.Println("after popdeps")
if err != nil { if err != nil {
fmt.Println("err", err) fmt.Println("err", err)
return err return err

View File

@ -0,0 +1,37 @@
package hapi
import (
"embed"
"html/template"
logging "github.com/ipfs/go-log/v2"
"github.com/filecoin-project/lotus/cmd/lotus-provider/deps"
"github.com/gorilla/mux"
"golang.org/x/xerrors"
)
//go:embed web/*
var templateFS embed.FS
func Routes(r *mux.Router, deps *deps.Deps) error {
t := new(template.Template)
t, err := t.ParseFS(templateFS, "web/*")
if err != nil {
return xerrors.Errorf("parse templates: %w", err)
}
a := &app{
db: deps.DB,
t: t,
}
r.HandleFunc("/simpleinfo/actorsummary", a.actorSummary)
r.HandleFunc("/simpleinfo/machines", a.indexMachines)
r.HandleFunc("/simpleinfo/tasks", a.indexTasks)
r.HandleFunc("/simpleinfo/taskhistory", a.indexTasksHistory)
return nil
}
var log = logging.Logger("lpweb")

View File

@ -0,0 +1,187 @@
package hapi
import (
"context"
"html/template"
"net/http"
"os"
"sync"
"time"
"github.com/filecoin-project/lotus/lib/harmony/harmonydb"
)
type app struct {
db *harmonydb.DB
t *template.Template
actorInfoLk sync.Mutex
actorInfos []actorInfo
}
type actorInfo struct {
Address string
CLayers []string
QualityAdjustedPower string
RawBytePower string
Deadlines []actorDeadline
}
type actorDeadline struct {
Empty bool
Current bool
Proven bool
PartFaulty bool
Faulty bool
}
func (a *app) actorSummary(w http.ResponseWriter, r *http.Request) {
a.actorInfoLk.Lock()
defer a.actorInfoLk.Unlock()
a.executeTemplate(w, "actor_summary", a.actorInfos)
}
func (a *app) indexMachines(w http.ResponseWriter, r *http.Request) {
s, err := a.clusterMachineSummary(r.Context())
if err != nil {
log.Errorf("cluster machine summary: %v", err)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
a.executeTemplate(w, "cluster_machines", s)
}
func (a *app) indexTasks(w http.ResponseWriter, r *http.Request) {
s, err := a.clusterTaskSummary(r.Context())
if err != nil {
log.Errorf("cluster task summary: %v", err)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
a.executeTemplate(w, "cluster_tasks", s)
}
func (a *app) indexTasksHistory(w http.ResponseWriter, r *http.Request) {
s, err := a.clusterTaskHistorySummary(r.Context())
if err != nil {
log.Errorf("cluster task history summary: %v", err)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
a.executeTemplate(w, "cluster_task_history", s)
}
var templateDev = os.Getenv("LOTUS_HAPI_TEMPLATE_DEV") == "1"
func (a *app) executeTemplate(w http.ResponseWriter, name string, data interface{}) {
if templateDev {
fs := os.DirFS("./cmd/lotus-provider/web/hapi/web")
a.t = template.Must(template.ParseFS(fs, "web/*"))
}
if err := a.t.ExecuteTemplate(w, name, data); err != nil {
log.Errorf("execute template %s: %v", name, err)
http.Error(w, "internal server error", http.StatusInternalServerError)
}
}
type machineSummary struct {
Address string
ID int64
SinceContact string
}
type taskSummary struct {
Name string
SincePosted string
Owner *string
ID int64
}
type taskHistorySummary struct {
Name string
TaskID int64
Posted, Start, End string
Result bool
Err string
CompletedBy string
}
func (a *app) clusterMachineSummary(ctx context.Context) ([]machineSummary, error) {
rows, err := a.db.Query(ctx, "SELECT id, host_and_port, last_contact FROM harmony_machines")
if err != nil {
return nil, err // Handle error
}
defer rows.Close()
var summaries []machineSummary
for rows.Next() {
var m machineSummary
var lastContact time.Time
if err := rows.Scan(&m.ID, &m.Address, &lastContact); err != nil {
return nil, err // Handle error
}
m.SinceContact = time.Since(lastContact).Round(time.Second).String()
summaries = append(summaries, m)
}
return summaries, nil
}
func (a *app) clusterTaskSummary(ctx context.Context) ([]taskSummary, error) {
rows, err := a.db.Query(ctx, "SELECT id, name, update_time, owner_id FROM harmony_task")
if err != nil {
return nil, err // Handle error
}
defer rows.Close()
var summaries []taskSummary
for rows.Next() {
var t taskSummary
var posted time.Time
if err := rows.Scan(&t.ID, &t.Name, &posted, &t.Owner); err != nil {
return nil, err // Handle error
}
t.SincePosted = time.Since(posted).Round(time.Second).String()
summaries = append(summaries, t)
}
return summaries, nil
}
func (a *app) clusterTaskHistorySummary(ctx context.Context) ([]taskHistorySummary, error) {
rows, err := a.db.Query(ctx, "SELECT id, name, task_id, posted, work_start, work_end, result, err, completed_by_host_and_port FROM harmony_task_history ORDER BY work_end DESC LIMIT 15")
if err != nil {
return nil, err // Handle error
}
defer rows.Close()
var summaries []taskHistorySummary
for rows.Next() {
var t taskHistorySummary
var posted, start, end time.Time
if err := rows.Scan(&t.TaskID, &t.Name, &t.TaskID, &posted, &start, &end, &t.Result, &t.Err, &t.CompletedBy); err != nil {
return nil, err // Handle error
}
t.Posted = posted.Round(time.Second).Format("02 Jan 06 15:04")
t.Start = start.Round(time.Second).Format("02 Jan 06 15:04")
t.End = end.Round(time.Second).Format("02 Jan 06 15:04")
summaries = append(summaries, t)
}
return summaries, nil
}

View File

@ -0,0 +1,20 @@
{{define "actor_summary"}}
{{range .}}
<tr>
<td>{{.Address}}</td>
<td>
{{range .CLayers}}
<span>{{.}} </span>
{{end}}
</td>
<td>{{.QualityAdjustedPower}}</td>
<td>
<div class="deadline-box">
{{range .Deadlines}}
<div class="deadline-entry{{if .Current}} deadline-entry-cur{{end}}{{if .Proven}} deadline-proven{{end}}{{if .PartFaulty}} deadline-partially-faulty{{end}}{{if .Faulty}} deadline-faulty{{end}}"></div>
{{end}}
</div>
</td>
</tr>
{{end}}
{{end}}

View File

@ -0,0 +1,15 @@
{{define "chain_rpcs"}}
{{range .}}
<tr>
<td>{{.Address}}</td>
<td>
{{range .CLayers}}
<span>{{.}} </span>
{{end}}
</td>
<td>{{if .Reachable}}<span class="success">ok</span>{{else}}<span class="error">FAIL</span>{{end}}</td>
<td>{{if eq "ok" .SyncState}}<span class="success">ok</span>{{else}}<span class="warning">{{.SyncState}}</span>{{end}}</td>
<td>{{.Version}}</td>
</tr>
{{end}}
{{end}}

View File

@ -0,0 +1,10 @@
{{define "cluster_machines"}}
{{range .}}
<tr>
<td>{{.Address}}</td>
<td>{{.ID}}</td>
<td>todo</td>
<td>{{.SinceContact}}</td>
</tr>
{{end}}
{{end}}

View File

@ -0,0 +1,14 @@
{{define "cluster_task_history"}}
{{range .}}
<tr>
<td>{{.Name}}</td>
<td>{{.TaskID}}</td>
<td>{{.CompletedBy}}</td>
<td>{{.Posted}}</td>
<td>{{.Start}}</td>
<td>{{.End}}</td>
<td>{{if .Result}}<span class="success">success</span>{{else}}<span class="error">error</span>{{end}}</td>
<td>{{.Err}}</td>
</tr>
{{end}}
{{end}}

View File

@ -0,0 +1,10 @@
{{define "cluster_tasks"}}
{{range .}}
<tr>
<td>{{.Name}}</td>
<td>{{.ID}}</td>
<td>{{.SincePosted}}</td>
<td>{{.Owner}}</td>
</tr>
{{end}}
{{end}}

View File

@ -10,6 +10,7 @@ import (
"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/api" "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/filecoin-project/lotus/metrics"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"go.opencensus.io/tag" "go.opencensus.io/tag"
@ -21,6 +22,10 @@ var static embed.FS
func GetSrv(ctx context.Context, deps *deps.Deps) (*http.Server, error) { func GetSrv(ctx context.Context, deps *deps.Deps) (*http.Server, error) {
mux := mux.NewRouter() mux := mux.NewRouter()
api.Routes(mux.PathPrefix("/api").Subrouter(), deps) api.Routes(mux.PathPrefix("/api").Subrouter(), deps)
err := hapi.Routes(mux.PathPrefix("/hapi").Subrouter(), deps)
if err != nil {
return nil, err
}
mux.NotFoundHandler = http.FileServer(http.FS(static)) mux.NotFoundHandler = http.FileServer(http.FS(static))
return &http.Server{ return &http.Server{

View File

@ -1,31 +1,45 @@
<html> <html>
<head> <head>
<title>Lotus Provider Cluster Overview</title> <title>Lotus Provider Cluster Overview</title>
<script src="https://unpkg.com/htmx.org@1.9.5" integrity="sha384-xcuj3WpfgjlKF+FXhSQFQ0ZNr39ln+hwjN3npfM9VBnUskLolQAcN80McRIVOPuO" crossorigin="anonymous"></script>
<script type="module" src="chain-connectivity.js"></script> <script type="module" src="chain-connectivity.js"></script>
<style> <style>
html, html, body {
body {
background: #0f0f0f; background: #0f0f0f;
color: #ffffff; color: #ffffff;
padding: 0; padding: 0;
margin: 0; margin: 0;
font-family: monospace; font-family: monospace;
} }
table td, table th {
font-size: 13px;
}
.app-head { .app-head {
width: 100%; width: 100%;
} }
.head-left { .head-left {
display: inline-block; display: inline-block;
} }
.head-right { .head-right {
display: inline-block; display: inline-block;
float: right; float: right;
} }
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;
}
a:link { a:link {
color: #cfc; color: #cfc;
@ -39,6 +53,16 @@
color: #af7; color: #af7;
} }
.success {
color: green;
}
.warning {
color: yellow;
}
.error {
color: red;
}
.dash-tile { .dash-tile {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -73,109 +97,97 @@
.deadline-proven { .deadline-proven {
background-color: green; background-color: green;
} }
.deadline-partially-faulty { .deadline-partially-faulty {
background-color: yellow; background-color: yellow;
} }
.deadline-faulty { .deadline-faulty {
background-color: red; background-color: red;
} }
</style> </style>
</head> </head>
<body> <body>
<div class="app-head"> <div class="app-head">
<div class="head-left"> <div class="head-left">
<h1>Lotus Provider Cluster</h1> <h1>Lotus Provider Cluster</h1>
</div>
<div class="head-right">
version [todo]
</div>
</div> </div>
<hr /> <div class="head-right">
<div class="page"> version [todo]
<div class="info-block"> </div>
<h2>Chain Connectivity</h2> </div>
<chain-connectivity></chain-connectivity> <hr/>
</div> <div class="page">
<div class="info-block">
<h2>Chain Connectivity</h2>
<chain-connectivity></chain-connectivity>
</div> </div>
<hr> <hr>
<div class="info-block"> <div class="info-block">
<h2>Actor Summary</h2> <h2>Actor Summary</h2>
<table> <table>
<thead> <thead>
<tr> <tr>
<th>Address</th> <th>Address</th>
<th>Config Layers</th> <th>Config Layers</th>
<th>QaP</th> <th>QaP</th>
<th>Deadlines</th> <th>Deadlines</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody hx-get="/hapi/simpleinfo/actorsummary" hx-trigger="load,every 5s">
<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> </tbody>
</table> </table>
</div> </div>
<hr>
<div class="info-block">
<h2>Cluster Machines</h2>
<table>
<thead>
<tr>
<th>Host</th>
<th>ID</th>
<th>Config Layers</th>
<th>Last Contact</th>
</tr>
</thead>
<tbody hx-get="/hapi/simpleinfo/machines" hx-trigger="load,every 5s">
</tbody>
</table>
</div> </div>
<hr>
<div class="info-block">
<h2>Recently Finished Tasks</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>ID</th>
<th>Executor</th>
<th>Posted</th>
<th>Start</th>
<th>End</th>
<th>Outcome</th>
<th>Message</th>
</tr>
</thead>
<tbody hx-get="/hapi/simpleinfo/taskhistory" hx-trigger="load, every 5s">
</tbody>
</table>
</div>
<hr>
<div class="info-block">
<h2>Cluster Tasks</h2>
<table>
<thead>
<tr>
<th>Task</th>
<th>ID</th>
<th>Posted</th>
<th>Owner</th>
</tr>
</thead>
<tbody hx-get="/hapi/simpleinfo/tasks" hx-trigger="load,every 5s">
</tbody>
</table>
</div>
</div>
</body> </body>
</html> </html>

View File

@ -184,6 +184,9 @@ retryRecordCompletion:
return false, fmt.Errorf("could not log completion: %w", err) return false, fmt.Errorf("could not log completion: %w", err)
} }
result = "" result = ""
if doErr != nil {
result = "non-failing error: " + doErr.Error()
}
} else { } else {
if doErr != nil { if doErr != nil {
result = "error: " + doErr.Error() result = "error: " + doErr.Error()

View File

@ -156,13 +156,18 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don
ComputeTime: details.CompTime, ComputeTime: details.CompTime,
} }
persistNoWin := func() error { persistNoWin := func() (bool, error) {
_, err := t.db.Exec(ctx, `UPDATE mining_base_block SET no_win = true WHERE task_id = $1`, taskID) n, err := t.db.Exec(ctx, `UPDATE mining_base_block SET no_win = true WHERE task_id = $1`, taskID)
if err != nil { 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)
if n == 0 {
return false, xerrors.Errorf("persist no win: no rows updated")
} }
return nil return false, nil
} }
// ensure we have a beacon entry for the epoch we're mining on // 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 { if mbi == nil {
// not eligible to mine on this base, we're done here // 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())) log.Debugw("WinPoSt not eligible to mine on this base", "tipset", types.LogCids(base.TipSet.Cids()))
return true, persistNoWin() return persistNoWin()
} }
if !mbi.EligibleForMining { if !mbi.EligibleForMining {
// slashed or just have no power yet, we're done here // slashed or just have no power yet, we're done here
log.Debugw("WinPoSt not eligible for mining", "tipset", types.LogCids(base.TipSet.Cids())) log.Debugw("WinPoSt not eligible for mining", "tipset", types.LogCids(base.TipSet.Cids()))
return true, persistNoWin() return persistNoWin()
} }
if len(mbi.Sectors) == 0 { if len(mbi.Sectors) == 0 {
@ -217,7 +222,7 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don
if eproof == nil { if eproof == nil {
// not a winner, we're done here // not a winner, we're done here
log.Debugw("WinPoSt not a winner", "tipset", types.LogCids(base.TipSet.Cids())) log.Debugw("WinPoSt not a winner", "tipset", types.LogCids(base.TipSet.Cids()))
return true, persistNoWin() return persistNoWin()
} }
} }