From 02bebe605a56c631df2ab10c53954fb38e5a9188 Mon Sep 17 00:00:00 2001 From: lash Date: Wed, 26 Mar 2025 18:17:54 +0000 Subject: [PATCH 1/4] Add reset tool --- devtools/admin/main.go | 17 +++++++++ go.mod | 2 +- go.sum | 4 +- internal/cmd/cmd.go | 85 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 3 deletions(-) diff --git a/devtools/admin/main.go b/devtools/admin/main.go index c3ce57f..7d42584 100644 --- a/devtools/admin/main.go +++ b/devtools/admin/main.go @@ -8,6 +8,7 @@ import ( "path" "git.defalsify.org/vise.git/logging" + "git.defalsify.org/vise.git/engine" "git.grassecon.net/grassrootseconomics/sarafu-vise/config" "git.grassecon.net/grassrootseconomics/sarafu-vise/handlers/application" @@ -18,6 +19,7 @@ import ( var ( logg = logging.NewVanilla().WithContextKey("SessionId") scriptDir = path.Join("services", "registration") + menuSeparator = ": " ) func main() { @@ -25,6 +27,8 @@ func main() { override := config.NewOverride() var sessionId string + var size uint + var engineDebug bool flag.StringVar(&sessionId, "session-id", "075xx2123", "session id") flag.StringVar(&override.DbConn, "c", "?", "default connection string (replaces all unspecified strings)") @@ -32,6 +36,8 @@ func main() { flag.StringVar(&override.UserConn, "userdata", "?", "userdata store connection string") flag.StringVar(&override.StateConn, "state", "?", "state store connection string") + flag.BoolVar(&engineDebug, "d", false, "use engine debug output") + flag.UintVar(&size, "s", 160, "max size of output") flag.Parse() config.Apply(override) @@ -50,7 +56,18 @@ func main() { os.Exit(1) } + cfg := engine.Config{ + Root: "root", + SessionId: sessionId, + OutputSize: uint32(size), + FlagCount: uint32(128), + MenuSeparator: menuSeparator, + EngineDebug: engineDebug, + ResetOnEmptyInput: true, + } + x := cmd.NewCmd(sessionId, flagParser) + x = x.WithEngine(cfg) err = x.Parse(flag.Args()) if err != nil { fmt.Fprintf(os.Stderr, "cmd parse fail: %v\n", err) diff --git a/go.mod b/go.mod index 1b06b27..e6bd876 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module git.grassecon.net/grassrootseconomics/sarafu-vise go 1.23.4 require ( - git.defalsify.org/vise.git v0.3.1 + git.defalsify.org/vise.git v0.3.2-0.20250326034808-b9c2294cbf1a git.grassecon.net/grassrootseconomics/common v0.0.0-20250121134736-ba8cbbccea7d git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250310093912-8145b4bd004b git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2 diff --git a/go.sum b/go.sum index 19c67fe..f6c0ec5 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -git.defalsify.org/vise.git v0.3.1 h1:A6FhMcur09ft/JzUPGXR+KpA17fltfeBnasyvLMZmq4= -git.defalsify.org/vise.git v0.3.1/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= +git.defalsify.org/vise.git v0.3.2-0.20250326034808-b9c2294cbf1a h1:5uLBUZC6armYgBPkuNEsQPtqT3qZxRfNP4HUdkhjm4I= +git.defalsify.org/vise.git v0.3.2-0.20250326034808-b9c2294cbf1a/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= git.grassecon.net/grassrootseconomics/common v0.0.0-20250121134736-ba8cbbccea7d h1:5mzLas+jxTUtusOKx4XvU+n2QvrV/mH17MnJRy46siQ= git.grassecon.net/grassrootseconomics/common v0.0.0-20250121134736-ba8cbbccea7d/go.mod h1:wgQJZGIS6QuNLHqDhcsvehsbn5PvgV7aziRebMnJi60= git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250310093912-8145b4bd004b h1:xiTpaqWWoF5qcnarY/9ZkT6aVdnKwqztb2gzIahJn4w= diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go index ac286c1..2fa0477 100644 --- a/internal/cmd/cmd.go +++ b/internal/cmd/cmd.go @@ -5,6 +5,10 @@ import ( "fmt" "git.defalsify.org/vise.git/logging" + "git.defalsify.org/vise.git/engine" + "git.defalsify.org/vise.git/persist" + "git.defalsify.org/vise.git/resource" + "git.defalsify.org/vise.git/state" "git.grassecon.net/grassrootseconomics/sarafu-vise/handlers/application" "git.grassecon.net/grassrootseconomics/visedriver/storage" ) @@ -20,6 +24,8 @@ type Cmd struct { cmd int enable bool exec func(ctx context.Context, ss storage.StorageService) error + engineConfig *engine.Config + st *state.State } func NewCmd(sessionId string, flagParser *application.FlagManager) *Cmd { @@ -29,10 +35,71 @@ func NewCmd(sessionId string, flagParser *application.FlagManager) *Cmd { } } +func (c *Cmd) WithEngine(engineConfig engine.Config) *Cmd { + c.engineConfig = &engineConfig + return c +} + func (c *Cmd) Exec(ctx context.Context, ss storage.StorageService) error { return c.exec(ctx, ss) } +func (c *Cmd) engine(ctx context.Context, rs resource.Resource, pe *persist.Persister) (engine.Engine, error) { + if c.engineConfig == nil { + return nil, fmt.Errorf("engine config missing") + } + en := engine.NewEngine(*c.engineConfig, rs) + + st := pe.GetState() + if st == nil { + return nil, fmt.Errorf("persister state fail") + } + en = en.WithState(st) + st.UseDebug() + ca := pe.GetMemory() + if ca == nil { + return nil, fmt.Errorf("persister cache fail") + } + en = en.WithMemory(ca) + logg.DebugCtxf(ctx, "state loaded", "state", st) + return en, nil +} + +func (c *Cmd) execReset(ctx context.Context, ss storage.StorageService) error { + pe, err := ss.GetPersister(ctx) + if err != nil { + return fmt.Errorf("get persister error: %v", err) + } + rs, err := ss.GetResource(ctx) + if err != nil { + return fmt.Errorf("get resource error: %v", err) + } + dbResource, ok := rs.(*resource.DbResource) + if !ok { + return fmt.Errorf("get dbresource error: %v", err) + } + err = pe.Load(c.engineConfig.SessionId) + if err != nil { + return fmt.Errorf("persister load error: %v", err) + } + en, err := c.engine(ctx, dbResource, pe) + if err != nil { + return err + } + _, err = en.(*engine.DefaultEngine).Reset(ctx, false) + if err != nil { + return err + } + st := pe.GetState() + logg.DebugCtxf(ctx, "state after reset", "state", st) + + err = pe.Save(c.engineConfig.SessionId) + if err != nil { + return err + } + return nil +} + func (c *Cmd) execAdmin(ctx context.Context, ss storage.StorageService) error { pe, err := ss.GetPersister(ctx) if err != nil { @@ -76,6 +143,15 @@ func (c *Cmd) parseCmdAdmin(cmd string, param string, more []string) (bool, erro return false, nil } +func (c *Cmd) parseCmdReset(cmd string, param string, more []string) (bool, error) { + if cmd == "reset" { + c.enable = false + c.exec = c.execReset + return true, nil + } + return false, nil +} + func (c *Cmd) Parse(args []string) error { if len(args) < 2 { return fmt.Errorf("Wrong number of arguments: %v", args) @@ -92,5 +168,14 @@ func (c *Cmd) Parse(args []string) error { return nil } + r, err = c.parseCmdReset(cmd, param, args) + if err != nil { + return err + } + if r { + return nil + } + + return fmt.Errorf("unknown subcommand: %s", cmd) } -- 2.45.2 From e064ec40de3773cc8225e98dfa73553e992cba78 Mon Sep 17 00:00:00 2001 From: lash Date: Wed, 26 Mar 2025 18:28:06 +0000 Subject: [PATCH 2/4] Check argument count in devtools admin --- internal/cmd/cmd.go | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go index 2fa0477..5c550ea 100644 --- a/internal/cmd/cmd.go +++ b/internal/cmd/cmd.go @@ -13,6 +13,11 @@ import ( "git.grassecon.net/grassrootseconomics/visedriver/storage" ) +var argc map[string]int = map[string]int{ + "reset": 0, + "admin": 1, +} + var ( logg = logging.NewVanilla().WithDomain("cmd").WithContextKey("SessionId") ) @@ -153,12 +158,23 @@ func (c *Cmd) parseCmdReset(cmd string, param string, more []string) (bool, erro } func (c *Cmd) Parse(args []string) error { - if len(args) < 2 { + var param string + if len(args) < 1 { return fmt.Errorf("Wrong number of arguments: %v", args) } cmd := args[0] - param := args[1] - args = args[2:] + + n, ok := argc[cmd] + if !ok { + return fmt.Errorf("invalid command: %v", cmd) + } + if n > 0 { + if len(args) < n + 1 { + return fmt.Errorf("Wrong number of arguments, need: %d", n) + } + param = args[1] + args = args[2:] + } r, err := c.parseCmdAdmin(cmd, param, args) if err != nil { -- 2.45.2 From fbd50145fcde7ce95388c731c50a810aa10286f3 Mon Sep 17 00:00:00 2001 From: lash Date: Thu, 3 Apr 2025 03:03:26 +0100 Subject: [PATCH 3/4] Add state cloner --- cmd/africastalking/main.go | 8 ++-- cmd/async/main.go | 8 ++-- cmd/http/main.go | 8 ++-- cmd/ssh/main.go | 8 ++-- config/config.go | 3 +- devtools/admin/main.go | 7 ++- internal/cmd/cmd.go | 88 +++++++++++++++++++++++++++++++++----- profile/profile_test.go | 6 +-- ssh/ssh.go | 4 +- 9 files changed, 102 insertions(+), 38 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 3f7e372..a1f4a0e 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -77,10 +77,10 @@ func main() { pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ - Root: "root", - OutputSize: uint32(size), - FlagCount: uint32(128), - MenuSeparator: menuSeparator, + Root: "root", + OutputSize: uint32(size), + FlagCount: uint32(128), + MenuSeparator: menuSeparator, ResetOnEmptyInput: true, } diff --git a/cmd/async/main.go b/cmd/async/main.go index 9a064a6..f131605 100644 --- a/cmd/async/main.go +++ b/cmd/async/main.go @@ -92,10 +92,10 @@ func main() { pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ - Root: "root", - OutputSize: uint32(size), - FlagCount: uint32(128), - MenuSeparator: menuSeparator, + Root: "root", + OutputSize: uint32(size), + FlagCount: uint32(128), + MenuSeparator: menuSeparator, ResetOnEmptyInput: true, } diff --git a/cmd/http/main.go b/cmd/http/main.go index 13ef408..303684d 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -78,10 +78,10 @@ func main() { pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ - Root: "root", - OutputSize: uint32(size), - FlagCount: uint32(128), - MenuSeparator: menuSeparator, + Root: "root", + OutputSize: uint32(size), + FlagCount: uint32(128), + MenuSeparator: menuSeparator, ResetOnEmptyInput: true, } diff --git a/cmd/ssh/main.go b/cmd/ssh/main.go index 96dd150..7588531 100644 --- a/cmd/ssh/main.go +++ b/cmd/ssh/main.go @@ -38,7 +38,7 @@ func main() { var stateDebug bool var host string var port uint - + flag.StringVar(&override.DbConn, "c", "?", "default connection string (replaces all unspecified strings)") flag.StringVar(&override.ResourceConn, "resource", "?", "resource connection string") flag.StringVar(&override.UserConn, "userdata", "?", "userdata store connection string") @@ -81,9 +81,9 @@ func main() { pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ - Root: "root", - OutputSize: uint32(size), - FlagCount: uint32(128), + Root: "root", + OutputSize: uint32(size), + FlagCount: uint32(128), ResetOnEmptyInput: true, } if stateDebug { diff --git a/config/config.go b/config/config.go index 863e418..cf33ffe 100644 --- a/config/config.go +++ b/config/config.go @@ -25,7 +25,7 @@ const ( defaultSSHHost string = "127.0.0.1" defaultSSHPort uint = 7122 defaultHTTPHost string = "127.0.0.1" - defaultHTTPPort uint = 7123 + defaultHTTPPort uint = 7123 defaultDomain = "sarafu.local" ) @@ -52,7 +52,6 @@ func SearchDomains() []string { return ParsedDomains } - func Language() string { return viseconfig.DefaultLanguage } diff --git a/devtools/admin/main.go b/devtools/admin/main.go index 7d42584..8c81609 100644 --- a/devtools/admin/main.go +++ b/devtools/admin/main.go @@ -7,8 +7,8 @@ import ( "os" "path" - "git.defalsify.org/vise.git/logging" "git.defalsify.org/vise.git/engine" + "git.defalsify.org/vise.git/logging" "git.grassecon.net/grassrootseconomics/sarafu-vise/config" "git.grassecon.net/grassrootseconomics/sarafu-vise/handlers/application" @@ -17,8 +17,8 @@ import ( ) var ( - logg = logging.NewVanilla().WithContextKey("SessionId") - scriptDir = path.Join("services", "registration") + logg = logging.NewVanilla().WithContextKey("SessionId") + scriptDir = path.Join("services", "registration") menuSeparator = ": " ) @@ -87,5 +87,4 @@ func main() { fmt.Fprintf(os.Stderr, "cmd exec error: %v\n", err) os.Exit(1) } - } diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go index 5c550ea..255e270 100644 --- a/internal/cmd/cmd.go +++ b/internal/cmd/cmd.go @@ -3,9 +3,11 @@ package cmd import ( "context" "fmt" + "regexp" - "git.defalsify.org/vise.git/logging" + "git.defalsify.org/vise.git/db" "git.defalsify.org/vise.git/engine" + "git.defalsify.org/vise.git/logging" "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/state" @@ -16,21 +18,24 @@ import ( var argc map[string]int = map[string]int{ "reset": 0, "admin": 1, + "clone": 1, } var ( - logg = logging.NewVanilla().WithDomain("cmd").WithContextKey("SessionId") + logg = logging.NewVanilla().WithDomain("cmd").WithContextKey("SessionId") + cloneTargetRegex = `^\+000` ) type Cmd struct { - sessionId string - conn storage.ConnData - flagParser *application.FlagManager - cmd int - enable bool - exec func(ctx context.Context, ss storage.StorageService) error - engineConfig *engine.Config - st *state.State + sessionId string + conn storage.ConnData + flagParser *application.FlagManager + cmd int + enable bool + param string + exec func(ctx context.Context, ss storage.StorageService) error + engineConfig *engine.Config + st *state.State } func NewCmd(sessionId string, flagParser *application.FlagManager) *Cmd { @@ -70,6 +75,50 @@ func (c *Cmd) engine(ctx context.Context, rs resource.Resource, pe *persist.Pers return en, nil } +func (c *Cmd) execClone(ctx context.Context, ss storage.StorageService) error { + re := regexp.MustCompile(cloneTargetRegex) + if !re.MatchString(c.param) { + return fmt.Errorf("Clone sessionId must match target: %s", c.param) + } + + pe, err := ss.GetPersister(ctx) + if err != nil { + return fmt.Errorf("get persister error: %v", err) + } + err = pe.Load(c.engineConfig.SessionId) + if err != nil { + return fmt.Errorf("persister load error: %v", err) + } + + /// TODO consider DRY with devtools/store/dump + store, err := ss.GetUserdataDb(ctx) + if err != nil { + return fmt.Errorf("store retrieve error: %v", err) + } + + store.SetSession(c.engineConfig.SessionId) + store.SetPrefix(db.DATATYPE_USERDATA) + dmp, err := store.Dump(ctx, []byte("")) + if err != nil { + return fmt.Errorf("store dump fail: %v\n", err.Error()) + } + + for true { + store.SetSession(c.engineConfig.SessionId) + k, v := dmp.Next(ctx) + if k == nil { + break + } + store.SetSession(c.param) + err = store.Put(ctx, k, v) + if err != nil { + return fmt.Errorf("user data store clone failed on key: %x", k) + } + } + + return pe.Save(c.param) +} + func (c *Cmd) execReset(ctx context.Context, ss storage.StorageService) error { pe, err := ss.GetPersister(ctx) if err != nil { @@ -157,6 +206,16 @@ func (c *Cmd) parseCmdReset(cmd string, param string, more []string) (bool, erro return false, nil } +func (c *Cmd) parseCmdClone(cmd string, param string, more []string) (bool, error) { + if cmd == "clone" { + c.enable = false + c.param = param + c.exec = c.execClone + return true, nil + } + return false, nil +} + func (c *Cmd) Parse(args []string) error { var param string if len(args) < 1 { @@ -169,7 +228,7 @@ func (c *Cmd) Parse(args []string) error { return fmt.Errorf("invalid command: %v", cmd) } if n > 0 { - if len(args) < n + 1 { + if len(args) < n+1 { return fmt.Errorf("Wrong number of arguments, need: %d", n) } param = args[1] @@ -192,6 +251,13 @@ func (c *Cmd) Parse(args []string) error { return nil } + r, err = c.parseCmdClone(cmd, param, args) + if err != nil { + return err + } + if r { + return nil + } return fmt.Errorf("unknown subcommand: %s", cmd) } diff --git a/profile/profile_test.go b/profile/profile_test.go index a210646..e8fe1b0 100644 --- a/profile/profile_test.go +++ b/profile/profile_test.go @@ -10,9 +10,9 @@ import ( func TestInsertOrShift(t *testing.T) { tests := []struct { name string - profile Profile - index int - value string + profile Profile + index int + value string expected []string }{ { diff --git a/ssh/ssh.go b/ssh/ssh.go index bab538a..f6d1dbb 100644 --- a/ssh/ssh.go +++ b/ssh/ssh.go @@ -181,8 +181,8 @@ func (s *SshRunner) GetEngine(sessionId string) (engine.Engine, func(), error) { accountService := services.New(ctx, menuStorageService) _, err = lhs.GetHandler(accountService) if err != nil { - fmt.Fprintf(os.Stderr, "get accounts service handler: %v\n", err) - os.Exit(1) + fmt.Fprintf(os.Stderr, "get accounts service handler: %v\n", err) + os.Exit(1) } en := lhs.GetEngine(lhs.Cfg, rs, pe) closer := func() { -- 2.45.2 From fa6980f5459bd3ed18868868fd509f843c2c8d8f Mon Sep 17 00:00:00 2001 From: lash Date: Fri, 4 Apr 2025 01:15:02 +0100 Subject: [PATCH 4/4] Better formatting of dump --- devtools/admin/main.go | 1 - devtools/store/dump/main.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/devtools/admin/main.go b/devtools/admin/main.go index 8c81609..4594efd 100644 --- a/devtools/admin/main.go +++ b/devtools/admin/main.go @@ -73,7 +73,6 @@ func main() { fmt.Fprintf(os.Stderr, "cmd parse fail: %v\n", err) os.Exit(1) } - logg.Infof("start command", "conn", conns, "subcmd", x) menuStorageService := storage.NewMenuStorageService(conns) diff --git a/devtools/store/dump/main.go b/devtools/store/dump/main.go index 6844a22..338469b 100644 --- a/devtools/store/dump/main.go +++ b/devtools/store/dump/main.go @@ -24,7 +24,7 @@ func formatItem(k []byte, v []byte, sessionId string) (string, error) { if err != nil { return "", err } - s := fmt.Sprintf("%v\t%v\n", o.Label, string(v)) + s := fmt.Sprintf("%v\n\t%v\n", o.Label, string(v)) return s, nil } -- 2.45.2