erigon-pulse/diagnostics/db_access.go
ledgerwatch 9c9449a059
Mock of Erigon support - step 1 (#6930)
Co-authored-by: Alexey Sharp <alexeysharp@Alexeys-iMac.local>
2023-02-22 12:38:22 +00:00

140 lines
3.5 KiB
Go

package diagnostics
import (
"context"
"encoding/hex"
"fmt"
"io"
"net/http"
"github.com/ledgerwatch/erigon-lib/kv"
"github.com/ledgerwatch/erigon-lib/kv/mdbx"
"github.com/ledgerwatch/erigon/common/paths"
"github.com/urfave/cli/v2"
)
func SetupDbAccess(ctx *cli.Context) {
var dataDir string
if ctx.IsSet("datadir") {
dataDir = ctx.String("datadir")
} else {
dataDir = paths.DataDirForNetwork(paths.DefaultDataDir(), ctx.String("chain"))
}
http.HandleFunc("/debug/metrics/db/list", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
writeDbList(w, dataDir)
})
http.HandleFunc("/debug/metrics/db/tables", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
writeDbTables(w, r, dataDir)
})
http.HandleFunc("/debug/metrics/db/read", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
writeDbRead(w, r, dataDir)
})
}
func writeDbList(w io.Writer, dataDir string) {
fmt.Fprintf(w, "SUCCESS\n")
m := mdbx.PathDbMap()
for path := range m {
fmt.Fprintf(w, "%s\n", path)
}
}
func writeDbTables(w io.Writer, r *http.Request, dataDir string) {
if err := r.ParseForm(); err != nil {
fmt.Fprintf(w, "ERROR: parsing arguments: %v\n", err)
return
}
path := r.Form.Get("path")
if path == "" {
fmt.Fprintf(w, "ERROR: path argument is required - specify the relative path to an MDBX database directory")
return
}
m := mdbx.PathDbMap()
db, ok := m[path]
if !ok {
fmt.Fprintf(w, "ERROR: path %s is not in the list of allowed paths", path)
return
}
var tables []string
if err := db.View(context.Background(), func(tx kv.Tx) error {
var e error
tables, e = tx.ListBuckets()
if e != nil {
return e
}
return nil
}); err != nil {
fmt.Fprintf(w, "ERROR: listing tables in %s: %v\n", path, err)
return
}
fmt.Fprintf(w, "SUCCESS\n")
for _, table := range tables {
fmt.Fprintf(w, "%s\n", table)
}
}
func writeDbRead(w io.Writer, r *http.Request, dataDir string) {
if err := r.ParseForm(); err != nil {
fmt.Fprintf(w, "ERROR: parsing arguments: %v\n", err)
return
}
path := r.Form.Get("path")
if path == "" {
fmt.Fprintf(w, "ERROR: path argument is required - specify the relative path to an MDBX database directory")
return
}
m := mdbx.PathDbMap()
db, ok := m[path]
if !ok {
fmt.Fprintf(w, "ERROR: path %s is not in the list of allowed paths", path)
return
}
table := r.Form.Get("table")
if table == "" {
fmt.Fprintf(w, "ERROR: table argument is required - specify the table to read from")
return
}
var key []byte
var err error
keyHex := r.Form.Get("key")
if keyHex != "" {
if key, err = hex.DecodeString(keyHex); err != nil {
fmt.Fprintf(w, "ERROR: key [%s] argument may only contain hexadecimal digits: %v\n", keyHex, err)
return
}
}
var results []string
if err := db.View(context.Background(), func(tx kv.Tx) error {
c, e := tx.Cursor(table)
if e != nil {
return e
}
defer c.Close()
var k, v []byte
if key == nil {
if k, v, e = c.First(); err != nil {
return e
}
} else if k, v, e = c.Seek(key); e != nil {
return e
}
count := 0
for e == nil && k != nil && count < 256 {
results = append(results, fmt.Sprintf("%x | %x", k, v))
count++
k, v, e = c.Next()
}
return nil
}); err != nil {
fmt.Fprintf(w, "ERROR: reading table %s in %s: %v\n", table, path, err)
return
}
fmt.Fprintf(w, "SUCCESS\n")
for _, result := range results {
fmt.Fprintf(w, "%s\n", result)
}
}