2024-07-11 19:39:54 +03:00
|
|
|
package meta
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2024-11-11 12:33:31 +03:00
|
|
|
"encoding/binary"
|
|
|
|
"errors"
|
2024-07-11 19:39:54 +03:00
|
|
|
"fmt"
|
|
|
|
|
|
|
|
common "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal"
|
2024-11-11 12:33:31 +03:00
|
|
|
schemaCommon "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema/common"
|
2024-07-11 19:39:54 +03:00
|
|
|
schema "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/schema/metabase"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-lens/internal/tui"
|
|
|
|
"github.com/rivo/tview"
|
|
|
|
"github.com/spf13/cobra"
|
2024-11-11 12:33:31 +03:00
|
|
|
"go.etcd.io/bbolt"
|
2024-07-11 19:39:54 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
var tuiCMD = &cobra.Command{
|
|
|
|
Use: "explore",
|
|
|
|
Short: "Metabase exploration with a terminal UI",
|
|
|
|
Long: `Launch a terminal UI to explore metabase and search for data.
|
|
|
|
|
|
|
|
Available search filters:
|
|
|
|
- cid CID
|
|
|
|
- oid OID
|
|
|
|
- addr CID/OID
|
|
|
|
- attr key[/value]
|
|
|
|
`,
|
|
|
|
Run: tuiFunc,
|
|
|
|
}
|
|
|
|
|
|
|
|
var initialPrompt string
|
|
|
|
|
2024-11-11 12:33:31 +03:00
|
|
|
var parserPerSchemaVersion = map[uint64]schemaCommon.Parser{
|
|
|
|
2: schema.MetabaseParserV2,
|
|
|
|
3: schema.MetabaseParserV3,
|
|
|
|
}
|
|
|
|
|
2024-07-11 19:39:54 +03:00
|
|
|
func init() {
|
|
|
|
common.AddComponentPathFlag(tuiCMD, &vPath)
|
|
|
|
|
|
|
|
tuiCMD.Flags().StringVar(
|
|
|
|
&initialPrompt,
|
|
|
|
"filter",
|
|
|
|
"",
|
|
|
|
"Filter prompt to start with, format 'tag:value [+ tag:value]...'",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
func tuiFunc(cmd *cobra.Command, _ []string) {
|
|
|
|
common.ExitOnErr(cmd, runTUI(cmd))
|
|
|
|
}
|
|
|
|
|
|
|
|
func runTUI(cmd *cobra.Command) error {
|
2024-10-07 18:32:26 +03:00
|
|
|
db, err := tui.OpenDB(vPath, false)
|
2024-07-11 19:39:54 +03:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("couldn't open database: %w", err)
|
|
|
|
}
|
|
|
|
defer db.Close()
|
|
|
|
|
2024-11-11 12:33:31 +03:00
|
|
|
schemaVersion, hasVersion := lookupSchemaVersion(cmd, db)
|
|
|
|
if !hasVersion {
|
|
|
|
return errors.New("couldn't detect schema version")
|
|
|
|
}
|
|
|
|
|
|
|
|
metabaseParser, ok := parserPerSchemaVersion[schemaVersion]
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("unknown schema version %d", schemaVersion)
|
|
|
|
}
|
|
|
|
|
2024-07-11 19:39:54 +03:00
|
|
|
// Need if app was stopped with Ctrl-C.
|
|
|
|
ctx, cancel := context.WithCancel(cmd.Context())
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
app := tview.NewApplication()
|
2024-11-11 12:33:31 +03:00
|
|
|
ui := tui.NewUI(ctx, app, db, metabaseParser, nil)
|
2024-07-11 19:39:54 +03:00
|
|
|
|
|
|
|
_ = ui.AddFilter("cid", tui.CIDParser, "CID")
|
|
|
|
_ = ui.AddFilter("oid", tui.OIDParser, "OID")
|
|
|
|
_ = ui.AddCompositeFilter("addr", tui.AddressParser, "CID/OID")
|
|
|
|
_ = ui.AddCompositeFilter("attr", tui.AttributeParser, "key[/value]")
|
|
|
|
|
|
|
|
err = ui.WithPrompt(initialPrompt)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("invalid filter prompt: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
app.SetRoot(ui, true).SetFocus(ui)
|
|
|
|
return app.Run()
|
|
|
|
}
|
2024-11-11 12:33:31 +03:00
|
|
|
|
|
|
|
var (
|
|
|
|
shardInfoBucket = []byte{5}
|
|
|
|
versionRecord = []byte("version")
|
|
|
|
)
|
|
|
|
|
|
|
|
func lookupSchemaVersion(cmd *cobra.Command, db *bbolt.DB) (version uint64, ok bool) {
|
|
|
|
err := db.View(func(tx *bbolt.Tx) error {
|
|
|
|
bkt := tx.Bucket(shardInfoBucket)
|
|
|
|
if bkt == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
rec := bkt.Get(versionRecord)
|
|
|
|
if rec == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
version = binary.LittleEndian.Uint64(rec)
|
|
|
|
ok = true
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
common.ExitOnErr(cmd, fmt.Errorf("couldn't lookup version: %w", err))
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|