From 5bc334fd2cdfd7488288e993ececb20aa427d57d Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 10 Jul 2025 18:03:30 +0300 Subject: [PATCH] Fixing updates --- .vscode/launch.json | 17 ++ README.md | 7 +- cmd/root.go | 16 +- cmd/run.go | 176 +++++++------- com/echo.lua | 4 - config-example.yaml | 2 +- core/config/compositor.go | 119 +++++++++- core/config/config.go | 21 +- core/config/consts.go | 21 +- core/corestate/node_uuid.go | 2 +- core/corestate/run_manager.go | 115 --------- core/corestate/types.go | 38 --- .../run_file_manager.go | 22 +- core/run_manager/run_manager.go | 158 ++++++++++++ core/sv1/handle_com.go | 4 +- core/sv1/handle_list.go | 4 +- core/update/update.go | 224 +++++++++--------- core/utils/routines.go | 27 ++- core/utils/utils_test.go | 48 ++++ core/utils/uuid.go | 6 +- main.go | 4 +- 21 files changed, 617 insertions(+), 418 deletions(-) create mode 100644 .vscode/launch.json delete mode 100644 core/corestate/run_manager.go rename core/{corestate => run_manager}/run_file_manager.go (75%) create mode 100644 core/run_manager/run_manager.go create mode 100644 core/utils/utils_test.go diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..778de42 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug Go program", + "type": "go", + "request": "launch", + "mode": "debug", + "program": "${workspaceFolder}", + "args": [ + "run", + "-t", "1,2,3" + ], + "env": {}, + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 1513dee..0530191 100644 --- a/README.md +++ b/README.md @@ -16,19 +16,18 @@ The basic directory tree looks something like this │   ├── echo.lua │   ├── _globals.lua Declaring global variables and functions for all internal scripts (also required for luarc to work correctly) │   └── _prepare.lua Script that is executed before each script launch -├── config.yaml -└── Makefile +└── config.yaml 3 directories, 6 files ``` Launch by command ```bash -./node run +./bin/node run ``` or for structured logs ```bash -./node run | jq +./bin/node run | jq ``` Example of GET request to server diff --git a/cmd/root.go b/cmd/root.go index 32418c4..b4f58ad 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,12 +1,18 @@ package cmd import ( + "fmt" "log" "os" + "github.com/akyaiy/GoSally-mvp/core/config" + "github.com/akyaiy/GoSally-mvp/core/corestate" + "github.com/akyaiy/GoSally-mvp/core/logs" "github.com/spf13/cobra" ) +var compositor *config.Compositor = config.NewCompositor() + var rootCmd = &cobra.Command{ Use: "node", Short: "Go Sally node", @@ -18,9 +24,11 @@ var rootCmd = &cobra.Command{ func Execute() { log.SetOutput(os.Stdout) - log.SetPrefix("\033[34m[INIT]\033[0m ") + log.SetPrefix(logs.SetBrightBlack(fmt.Sprintf("(%s) ", corestate.StageNotReady))) log.SetFlags(log.Ldate | log.Ltime) - if err := rootCmd.Execute(); err != nil { - log.Fatalf("Unexpected error: %s", err.Error()) - } + compositor.LoadCMDLine(rootCmd) + rootCmd.Execute() + // if err := rootCmd.Execute(); err != nil { + // log.Fatalf("Unexpected error: %s", err.Error()) + // } } diff --git a/cmd/run.go b/cmd/run.go index 1dac528..ce11dcd 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -13,7 +13,6 @@ import ( "os" "path/filepath" "regexp" - "strings" "syscall" "time" @@ -22,7 +21,9 @@ import ( "github.com/akyaiy/GoSally-mvp/core/corestate" gs "github.com/akyaiy/GoSally-mvp/core/general_server" "github.com/akyaiy/GoSally-mvp/core/logs" + "github.com/akyaiy/GoSally-mvp/core/run_manager" "github.com/akyaiy/GoSally-mvp/core/sv1" + "github.com/akyaiy/GoSally-mvp/core/update" "github.com/akyaiy/GoSally-mvp/core/utils" "github.com/go-chi/chi/v5" "github.com/go-chi/cors" @@ -39,6 +40,7 @@ var runCmd = &cobra.Command{ nodeApp.InitialHooks( func(cs *corestate.CoreState, x *app.AppX) { + x.Config = compositor x.Log.SetOutput(os.Stdout) x.Log.SetPrefix(logs.SetBrightBlack(fmt.Sprintf("(%s) ", cs.Stage))) x.Log.SetFlags(log.Ldate | log.Ltime) @@ -49,29 +51,27 @@ var runCmd = &cobra.Command{ *cs = *corestate.NewCorestate(&corestate.CoreState{ UUID32DirName: "uuid", NodeBinName: filepath.Base(os.Args[0]), - NodeVersion: config.GetUpdateConsts().GetNodeVersion(), + NodeVersion: config.NodeVersion, MetaDir: "./.meta", Stage: corestate.StagePreInit, - RM: corestate.NewRM(), StartTimestampUnix: time.Now().Unix(), }) }, func(cs *corestate.CoreState, x *app.AppX) { x.Log.SetPrefix(logs.SetBlue(fmt.Sprintf("(%s) ", cs.Stage))) - x.Config = config.NewCompositor() + if err := x.Config.LoadEnv(); err != nil { x.Log.Fatalf("env load error: %s", err) } cs.NodePath = x.Config.Env.NodePath - if cfgPath := config.ConfigPath; cfgPath != "" { + if cfgPath := x.Config.CMDLine.Run.ConfigPath; cfgPath != "" { x.Config.Env.ConfigPath = cfgPath } if err := x.Config.LoadConf(x.Config.Env.ConfigPath); err != nil { x.Log.Fatalf("conf load error: %s", err) } - }, func(cs *corestate.CoreState, x *app.AppX) { @@ -92,63 +92,54 @@ var runCmd = &cobra.Command{ }, func(cs *corestate.CoreState, x *app.AppX) { - if x.Config.Env.ParentStagePID != os.Getpid() || x.Config.Env.ParentStagePID == -1 { + if x.Config.Env.ParentStagePID != os.Getpid() { + if os.TempDir() != "/tmp" { + x.Log.Printf("%s: %s", logs.SetYellow("warning"), "non-standard value specified for temporary directory") + } // still pre-init stage - func(cs *corestate.CoreState, x *app.AppX) { - runDir, err := cs.RM.Create(cs.UUID32) - if err != nil { - x.Log.Fatalf("Unexpected failure: %s", err.Error()) - } - cs.RunDir = runDir - input, err := os.Open(os.Args[0]) - if err != nil { - cs.RM.Clean() - x.Log.Fatalf("Unexpected failure: %s", err.Error()) - } - if err := cs.RM.Set(cs.NodeBinName); err != nil { - cs.RM.Clean() - x.Log.Fatalf("Unexpected failure: %s", err.Error()) - } - fmgr := cs.RM.File(cs.NodeBinName) - output, err := fmgr.Open() - if err != nil { - cs.RM.Clean() - x.Log.Fatalf("Unexpected failure: %s", err.Error()) - } + runDir, err := run_manager.Create(cs.UUID32) + if err != nil { + x.Log.Fatalf("Unexpected failure: %s", err.Error()) + } + cs.RunDir = runDir + input, err := os.Open(os.Args[0]) + if err != nil { + run_manager.Clean() + x.Log.Fatalf("Unexpected failure: %s", err.Error()) + } + if err := run_manager.Set(cs.NodeBinName); err != nil { + run_manager.Clean() + x.Log.Fatalf("Unexpected failure: %s", err.Error()) + } + fmgr := run_manager.File(cs.NodeBinName) + output, err := fmgr.Open() + if err != nil { + run_manager.Clean() + x.Log.Fatalf("Unexpected failure: %s", err.Error()) + } - if _, err := io.Copy(output, input); err != nil { - fmgr.Close() - cs.RM.Clean() - x.Log.Fatalf("Unexpected failure: %s", err.Error()) - } - if err := os.Chmod(filepath.Join(cs.RunDir, cs.NodeBinName), 0755); err != nil { - fmgr.Close() - cs.RM.Clean() - x.Log.Fatalf("Unexpected failure: %s", err.Error()) - } - input.Close() + if _, err := io.Copy(output, input); err != nil { fmgr.Close() - runArgs := os.Args - runArgs[0] = filepath.Join(cs.RunDir, cs.NodeBinName) + run_manager.Clean() + x.Log.Fatalf("Unexpected failure: %s", err.Error()) + } + if err := os.Chmod(filepath.Join(cs.RunDir, cs.NodeBinName), 0755); err != nil { + fmgr.Close() + run_manager.Clean() + x.Log.Fatalf("Unexpected failure: %s", err.Error()) + } + input.Close() + fmgr.Close() + runArgs := os.Args + runArgs[0] = filepath.Join(cs.RunDir, cs.NodeBinName) - // prepare environ - env := os.Environ() + // prepare environ + env := utils.SetEviron(os.Environ(), fmt.Sprintf("GS_PARENT_PID=%d", os.Getpid())) - var filtered []string - for _, e := range env { - if strings.HasPrefix(e, "GS_PARENT_PID=") { - if e != "GS_PARENT_PID=-1" { - continue - } - } - filtered = append(filtered, e) - } - - if err := syscall.Exec(runArgs[0], runArgs, append(filtered, fmt.Sprintf("GS_PARENT_PID=%d", os.Getpid()))); err != nil { - cs.RM.Clean() - x.Log.Fatalf("Unexpected failure: %s", err.Error()) - } - }(cs, x) + if err := syscall.Exec(runArgs[0], runArgs, env); err != nil { + run_manager.Clean() + x.Log.Fatalf("Unexpected failure: %s", err.Error()) + } } x.Log.Printf("Node uuid is %s", cs.UUID32) }, @@ -158,30 +149,30 @@ var runCmd = &cobra.Command{ cs.Stage = corestate.StagePostInit x.Log.SetPrefix(logs.SetYellow(fmt.Sprintf("(%s) ", cs.Stage))) - cs.RunDir = cs.RM.Toggle() + cs.RunDir = run_manager.Toggle() exist, err := utils.ExistsMatchingDirs(filepath.Join(os.TempDir(), fmt.Sprintf("/*-%s-%s", cs.UUID32, "gosally-runtime")), cs.RunDir) if err != nil { - cs.RM.Clean() + run_manager.Clean() x.Log.Fatalf("Unexpected failure: %s", err.Error()) } if exist { - cs.RM.Clean() + run_manager.Clean() x.Log.Fatalf("Unable to continue node operation: A node with the same identifier was found in the runtime environment") } - if err := cs.RM.Set("run.lock"); err != nil { - cs.RM.Clean() + if err := run_manager.Set("run.lock"); err != nil { + run_manager.Clean() x.Log.Fatalf("Unexpected failure: %s", err.Error()) } - lockPath, err := cs.RM.Get("run.lock") + lockPath, err := run_manager.Get("run.lock") if err != nil { - cs.RM.Clean() + run_manager.Clean() x.Log.Fatalf("Unexpected failure: %s", err.Error()) } lockFile := ini.Empty() secRun, err := lockFile.NewSection("runtime") if err != nil { - cs.RM.Clean() + run_manager.Clean() x.Log.Fatalf("Unexpected failure: %s", err.Error()) } secRun.Key("pid").SetValue(fmt.Sprintf("%d/%d", os.Getpid(), x.Config.Env.ParentStagePID)) @@ -192,7 +183,7 @@ var runCmd = &cobra.Command{ err = lockFile.SaveTo(lockPath) if err != nil { - cs.RM.Clean() + run_manager.Clean() x.Log.Fatalf("Unexpected failure: %s", err.Error()) } }, @@ -208,22 +199,20 @@ var runCmd = &cobra.Command{ nodeApp.Run(func(ctx context.Context, cs *corestate.CoreState, x *app.AppX) error { ctxMain, cancelMain := context.WithCancel(ctx) - runLockFile := cs.RM.File("run.lock") + runLockFile := run_manager.File("run.lock") _, err := runLockFile.Open() if err != nil { x.Log.Fatalf("cannot open run.lock: %s", err) } - go func() { - err := runLockFile.Watch(ctxMain, func() { - x.Log.Printf("run.lock was touched") - cs.RM.Clean() - cancelMain() - }) - if err != nil { - x.Log.Printf("watch error: %s", err) - } - }() + _, err = runLockFile.Watch(ctxMain, func() { + x.Log.Printf("run.lock was touched") + run_manager.Clean() + cancelMain() + }) + if err != nil { + x.Log.Printf("watch error: %s", err) + } serverv1 := sv1.InitV1Server(&sv1.HandlerV1InitStruct{ Log: *x.SLog, @@ -246,7 +235,7 @@ var runCmd = &cobra.Command{ AllowCredentials: true, MaxAge: 300, })) - r.Route(config.GetServerConsts().GetApiRoute()+config.GetServerConsts().GetComDirRoute(), func(r chi.Router) { + r.Route(config.ApiRoute+config.ComDirRoute, func(r chi.Router) { r.Get("/", s.HandleList) r.Get("/{cmd}", s.Handle) }) @@ -285,27 +274,44 @@ var runCmd = &cobra.Command{ x.SLog.Error("Failed to start HTTP server", slog.String("error", err.Error())) } } + srv.Shutdown(ctxMain) }() - if err := srv.Shutdown(ctxMain); err != nil { - x.Log.Printf("%s", fmt.Sprintf("Failed to shutdown server gracefully: %s", err.Error())) - } else { - x.Log.Printf("The server shut down successfully") + if x.Config.Conf.Updates.UpdatesEnabled { + go func() { + x.Updated = update.NewUpdater(ctxMain, x.Log, x.Config.Conf, x.Config.Env) + x.Updated.Shutdownfunc(cancelMain) + for { + isNewUpdate, err := x.Updated.CkeckUpdates() + if err != nil { + x.Log.Printf("Failed to check for updates: %s", err.Error()) + } + if isNewUpdate { + if err := x.Updated.Update(); err != nil { + x.Log.Printf("Failed to update: %s", err.Error()) + } else { + x.Log.Printf("Update completed successfully") + } + } + time.Sleep(x.Config.Conf.Updates.CheckInterval) + } + }() } <-ctxMain.Done() + x.Log.Println("cleaning up...") - if err := cs.RM.Clean(); err != nil { + if err := run_manager.Clean(); err != nil { x.Log.Printf("cleanup error: %s", err) } x.Log.Println("bye!") + return nil }) }, } func init() { - runCmd.Flags().StringVarP(&config.ConfigPath, "config", "c", "./config.yaml", "Path to configuration file") rootCmd.AddCommand(runCmd) } diff --git a/com/echo.lua b/com/echo.lua index 52c31bb..04603e6 100644 --- a/com/echo.lua +++ b/com/echo.lua @@ -2,10 +2,6 @@ --- #args --- msg = the message -local os = require("os") - -os.execute("touch 1") - if not In.Params.msg or In.Params.msg == "" then Out.Result.status = Status.error Out.Result.error = "Missing parameter: msg" diff --git a/config-example.yaml b/config-example.yaml index e1111ff..bfbd421 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -16,6 +16,6 @@ tls: com_dir: "com/" updates: - enabled: false + enabled: true check-interval: 1h repository_url: "https://repo.serve.lv/raw/go-sally" \ No newline at end of file diff --git a/core/config/compositor.go b/core/config/compositor.go index bce2518..f7bbdc9 100644 --- a/core/config/compositor.go +++ b/core/config/compositor.go @@ -2,8 +2,11 @@ package config import ( "fmt" + "reflect" + "strconv" "strings" + "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -52,12 +55,6 @@ func (c *Compositor) LoadConf(path string) error { v.SetDefault("updates.check_interval", "2h") v.SetDefault("updates.wanted_version", "latest-stable") - // поддержка ENV-переопределений - v.SetEnvPrefix("GOSALLY") - v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) - v.AutomaticEnv() - - // читаем YAML if err := v.ReadInConfig(); err != nil { return fmt.Errorf("error reading config: %w", err) } @@ -67,6 +64,116 @@ func (c *Compositor) LoadConf(path string) error { return fmt.Errorf("error unmarshaling config: %w", err) } + c.Conf = &Conf{} c.Conf = &cfg return nil } + +func (c *Compositor) LoadCMDLine(root *cobra.Command) { + cmdLine := &CMDLine{} + c.CMDLine = cmdLine + + t := reflect.TypeOf(cmdLine).Elem() + v := reflect.ValueOf(cmdLine).Elem() + + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + fieldVal := v.Field(i) + ptr := fieldVal.Addr().Interface() + use := strings.ToLower(field.Name) + + var cmd *cobra.Command + for _, sub := range root.Commands() { + + if sub.Use == use { + cmd = sub + break + } + } + + if use == root.Use { + cmd = root + } + + if cmd == nil { + continue + } + + Unmarshal(cmd, ptr) + } +} + +func Unmarshal(cmd *cobra.Command, target any) { + t := reflect.TypeOf(target).Elem() + v := reflect.ValueOf(target).Elem() + + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + valPtr := v.Field(i).Addr().Interface() + + full := field.Tag.Get("full") + short := field.Tag.Get("short") + def := field.Tag.Get("def") + desc := field.Tag.Get("desc") + isPersistent := field.Tag.Get("persistent") == "true" + + flagSet := cmd.Flags() + if isPersistent { + flagSet = cmd.PersistentFlags() + } + + switch field.Type.Kind() { + case reflect.String: + flagSet.StringVarP(valPtr.(*string), full, short, def, desc) + + case reflect.Bool: + defVal, err := strconv.ParseBool(def) + if err != nil && def != "" { + fmt.Printf("warning: cannot parse default bool: %q\n", def) + } + flagSet.BoolVarP(valPtr.(*bool), full, short, defVal, desc) + + case reflect.Int: + defVal, err := strconv.Atoi(def) + if err != nil && def != "" { + fmt.Printf("warning: cannot parse default int: %q\n", def) + } + flagSet.IntVarP(valPtr.(*int), full, short, defVal, desc) + + case reflect.Slice: + elemKind := field.Type.Elem().Kind() + switch elemKind { + case reflect.String: + defVals := []string{} + if def != "" { + defVals = strings.Split(def, ",") + } + flagSet.StringSliceVarP(valPtr.(*[]string), full, short, defVals, desc) + + case reflect.Int: + var intVals []int + if def != "" { + for _, s := range strings.Split(def, ",") { + s = strings.TrimSpace(s) + if s == "" { + continue + } + n, err := strconv.Atoi(s) + if err != nil { + fmt.Printf("warning: cannot parse int in slice: %q\n", s) + continue + } + intVals = append(intVals, n) + } + } + flagSet.IntSliceVarP(valPtr.(*[]int), full, short, intVals, desc) + + default: + fmt.Printf("unsupported slice element type: %s\n", elemKind) + } + + default: + fmt.Printf("unsupported field type: %s\n", field.Type.Kind()) + } + } +} diff --git a/core/config/config.go b/core/config/config.go index f9ab69d..840a6e8 100644 --- a/core/config/config.go +++ b/core/config/config.go @@ -6,16 +6,15 @@ import ( "time" ) -var ConfigPath string - type CompositorContract interface { LoadEnv() error LoadConf(path string) error } type Compositor struct { - Conf *Conf - Env *Env + CMDLine *CMDLine + Conf *Conf + Env *Env } type Conf struct { @@ -57,3 +56,17 @@ type Env struct { NodePath string `mapstructure:"node_path"` ParentStagePID int `mapstructure:"parent_pid"` } + +type CMDLine struct { + Run Run + Node Root +} + +type Root struct { + Debug bool `persistent:"true" full:"debug" short:"d" def:"false" desc:"Set debug mode"` +} + +type Run struct { + ConfigPath string `persistent:"true" full:"config" short:"c" def:"./config.yaml" desc:"Path to configuration file"` + Test []int `persistent:"true" full:"test" short:"t" def:"" desc:"js test"` +} diff --git a/core/config/consts.go b/core/config/consts.go index 485a0ac..2990545 100644 --- a/core/config/consts.go +++ b/core/config/consts.go @@ -27,25 +27,8 @@ var UpdateDownloadPath string = os.TempDir() var MetaDir string = "./.meta" -type _internalConsts struct{} -type _serverConsts struct{} -type _updateConsts struct{} - -func GetUpdateConsts() _updateConsts { return _updateConsts{} } -func (_ _updateConsts) GetNodeVersion() string { +func init() { if NodeVersion == "" { - return "v0.0.0-none" + NodeVersion = "v0.0.0-none" } - return NodeVersion } -func (_ _updateConsts) GetActualFileName() string { return ActualFileName } -func (_ _updateConsts) GetUpdateArchiveName() string { return UpdateArchiveName } -func (_ _updateConsts) GetUpdateDownloadPath() string { return UpdateDownloadPath } - -func GetInternalConsts() _internalConsts { return _internalConsts{} } -func (_ _internalConsts) GetUUIDLength() int { return UUIDLength } -func (_ _internalConsts) GetMetaDir() string { return MetaDir } - -func GetServerConsts() _serverConsts { return _serverConsts{} } -func (_ _serverConsts) GetApiRoute() string { return ApiRoute } -func (_ _serverConsts) GetComDirRoute() string { return ComDirRoute } diff --git a/core/corestate/node_uuid.go b/core/corestate/node_uuid.go index 98e695e..88f2c32 100644 --- a/core/corestate/node_uuid.go +++ b/core/corestate/node_uuid.go @@ -28,7 +28,7 @@ func readNodeUUIDRaw(p string) ([]byte, error) { if err != nil { return data, err } - if len(data) != config.GetInternalConsts().GetUUIDLength() { + if len(data) != config.UUIDLength { return data, errors.New("decoded UUID length mismatch") } return data, nil diff --git a/core/corestate/run_manager.go b/core/corestate/run_manager.go deleted file mode 100644 index 0658735..0000000 --- a/core/corestate/run_manager.go +++ /dev/null @@ -1,115 +0,0 @@ -package corestate - -import ( - "fmt" - "os" - "path/filepath" - - "github.com/akyaiy/GoSally-mvp/core/utils" -) - -func NewRM() *RunManager { - return &RunManager{ - indexedPaths: func() *map[string]string { m := make(map[string]string); return &m }(), - created: false, - } -} - -func (c *CoreState) RuntimeDir() RunManagerContract { - return c.RM -} - -// Create creates a temp directory -func (r *RunManager) Create(uuid32 string) (string, error) { - if r.created { - return r.runDir, fmt.Errorf("runtime directory is already created") - } - path, err := os.MkdirTemp("", fmt.Sprintf("*-%s-%s", uuid32, "gosally-runtime")) - if err != nil { - return "", err - } - r.runDir = path - r.created = true - return path, nil -} - -func (r *RunManager) Clean() error { - return utils.CleanTempRuntimes(r.runDir) -} - -// Quite dangerous and goofy. -// TODO: implement a better variant of runDir indexing on the second stage of initialization -func (r *RunManager) Toggle() string { - r.runDir = filepath.Dir(os.Args[0]) - r.created = true - return r.runDir -} - -func (r *RunManager) Get(index string) (string, error) { - if !r.created { - return "", fmt.Errorf("runtime directory is not created") - } - if r.indexedPaths == nil { - err := r.indexPaths() - if err != nil { - return "", nil - } - } - if r.indexedPaths == nil { - return "", fmt.Errorf("indexedPaths is nil") - } - value, ok := (*r.indexedPaths)[index] - if !ok { - err := r.indexPaths() - if err != nil { - return "", err - } - value, ok = (*r.indexedPaths)[index] - if !ok { - return "", fmt.Errorf("cannot detect file under index %s", index) - } - } - return value, nil -} - -func (r *RunManager) Set(index string) error { - if !r.created { - return fmt.Errorf("runtime directory is not created") - } - fullPath := filepath.Join(r.runDir, index) - - dir := filepath.Dir(fullPath) - err := os.MkdirAll(dir, 0755) - if err != nil { - return err - } - - f, err := os.OpenFile(fullPath, os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - return err - } - defer f.Close() - - if r.indexedPaths == nil { - err = r.indexPaths() - if err != nil { - return err - } - } else { - (*r.indexedPaths)[index] = fullPath - } - - return nil -} - -func (r *RunManager) indexPaths() error { - if !r.created { - return fmt.Errorf("runtime directory is not created") - } - i, err := utils.IndexPaths(r.runDir) - if err != nil { - return err - } - r.indexedPaths = i - return nil -} diff --git a/core/corestate/types.go b/core/corestate/types.go index ba8f7c3..b120d4f 100644 --- a/core/corestate/types.go +++ b/core/corestate/types.go @@ -1,16 +1,10 @@ package corestate -import ( - "context" - "os" -) - // CoreStateContract is interface for CoreState. // CoreState is a structure that contains the basic meta-information vital to the node. // The interface contains functionality for working with the Runtime directory and its files, // and access to low-level logging in stdout type CoreStateContract interface { - RuntimeDir() RunManagerContract } type CoreState struct { @@ -27,36 +21,4 @@ type CoreState struct { NodePath string MetaDir string RunDir string - - RM *RunManager -} - -type RunManagerContract interface { - Get(index string) (string, error) - - // Set recursively creates a file in runDir - Set(index string) error - - File(index string) RunFileManagerContract - - indexPaths() error -} - -type RunManager struct { - created bool - runDir string - // I obviously keep it with a pointer because it makes me feel calmer - indexedPaths *map[string]string -} - -type RunFileManagerContract interface { - Open() (*os.File, error) - Close() error - Watch(ctx context.Context, callback func()) error -} - -type RunFileManager struct { - err error - indexedPath string - file *os.File } diff --git a/core/corestate/run_file_manager.go b/core/run_manager/run_file_manager.go similarity index 75% rename from core/corestate/run_file_manager.go rename to core/run_manager/run_file_manager.go index 413ea1e..d920c61 100644 --- a/core/corestate/run_file_manager.go +++ b/core/run_manager/run_file_manager.go @@ -1,4 +1,4 @@ -package corestate +package run_manager import ( "context" @@ -8,16 +8,16 @@ import ( "time" ) -func (r *RunManager) File(index string) RunFileManagerContract { - value, ok := (*r.indexedPaths)[index] +func File(index string) RunFileManagerContract { + value, ok := indexedPaths[index] if !ok { - err := r.indexPaths() + err := indexPaths() if err != nil { return &RunFileManager{ err: err, } } - value, ok = (*r.indexedPaths)[index] + value, ok = indexedPaths[index] if !ok { return &RunFileManager{ err: fmt.Errorf("cannot detect file under index %s", index), @@ -45,22 +45,24 @@ func (r *RunFileManager) Close() error { return r.file.Close() } -func (r *RunFileManager) Watch(ctx context.Context, callback func()) error { +func (r *RunFileManager) Watch(parentCtx context.Context, callback func()) (context.CancelFunc, error) { if r.err != nil { - return r.err + return nil, r.err } if r.file == nil { - return fmt.Errorf("file is not opened") + return nil, fmt.Errorf("file is not opened") } info, err := r.file.Stat() if err != nil { - return err + return nil, err } origStat := info.Sys().(*syscall.Stat_t) origIno := origStat.Ino origModTime := info.ModTime() + ctx, cancel := context.WithCancel(parentCtx) + go func() { for { select { @@ -89,5 +91,5 @@ func (r *RunFileManager) Watch(ctx context.Context, callback func()) error { } }() - return nil + return cancel, nil } diff --git a/core/run_manager/run_manager.go b/core/run_manager/run_manager.go new file mode 100644 index 0000000..c60458b --- /dev/null +++ b/core/run_manager/run_manager.go @@ -0,0 +1,158 @@ +package run_manager + +import ( + "context" + "fmt" + "os" + "path/filepath" + + "github.com/akyaiy/GoSally-mvp/core/utils" +) + +type RunManagerContract interface { + Get(index string) (string, error) + + // Set recursively creates a file in runDir + Set(index string) error + + File(index string) RunFileManagerContract + + indexPaths() error +} + +var ( + created bool + runDir string + indexedPaths = make(map[string]string) +) + +type RunFileManagerContract interface { + Open() (*os.File, error) + Close() error + Watch(parentCtx context.Context, callback func()) (context.CancelFunc, error) +} + +type RunFileManager struct { + err error + indexedPath string + file *os.File +} + +// func (c *CoreState) RuntimeDir() RunManagerContract { +// return c.RM +// } + +// Create creates a temp directory +func Create(uuid32 string) (string, error) { + if created { + return runDir, fmt.Errorf("runtime directory is already created") + } + path, err := os.MkdirTemp("", fmt.Sprintf("*-%s-%s", uuid32, "gosally-runtime")) + if err != nil { + return "", err + } + runDir = path + created = true + return path, nil +} + +func Clean() error { + created = false + indexedPaths = nil + return utils.CleanTempRuntimes(runDir) +} + +// Quite dangerous and goofy. +// TODO: implement a better variant of runDir indexing on the second stage of initialization +func Toggle() string { + runDir = filepath.Dir(os.Args[0]) + created = true + return runDir +} + +func Get(index string) (string, error) { + if !created { + return "", fmt.Errorf("runtime directory is not created") + } + if indexedPaths == nil { + err := indexPaths() + if err != nil { + return "", nil + } + } + if indexedPaths == nil { + return "", fmt.Errorf("indexedPaths is nil") + } + value, ok := indexedPaths[index] + if !ok { + err := indexPaths() + if err != nil { + return "", err + } + value, ok = indexedPaths[index] + if !ok { + return "", fmt.Errorf("cannot detect file under index %s", index) + } + } + return value, nil +} + +func Set(index string) error { + if !created { + return fmt.Errorf("runtime directory is not created") + } + fullPath := filepath.Join(runDir, index) + + dir := filepath.Dir(fullPath) + err := os.MkdirAll(dir, 0755) + if err != nil { + return err + } + + f, err := os.OpenFile(fullPath, os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return err + } + defer f.Close() + + if indexedPaths == nil { + err = indexPaths() + if err != nil { + return err + } + } else { + indexedPaths[index] = fullPath + } + + return nil +} + +func SetDir(index string) error { + if !created { + return fmt.Errorf("runtime directory is not created") + } + fullPath := filepath.Join(runDir, index) + + err := os.MkdirAll(fullPath, 0755) + if err != nil { + return err + } + + return nil +} + +func indexPaths() error { + if !created { + return fmt.Errorf("runtime directory is not created") + } + i, err := utils.IndexPaths(runDir) + if err != nil { + return err + } + indexedPaths = i + return nil +} + +func RuntimeDir() string { + return runDir +} diff --git a/core/sv1/handle_com.go b/core/sv1/handle_com.go index 2bcd4f2..0ee9f0a 100644 --- a/core/sv1/handle_com.go +++ b/core/sv1/handle_com.go @@ -18,7 +18,7 @@ import ( // The function processes the HTTP request and runs Lua scripts, // preparing the environment and subsequently transmitting the execution result func (h *HandlerV1) Handle(w http.ResponseWriter, r *http.Request) { - uuid16, err := utils.NewUUID(int(config.GetInternalConsts().GetUUIDLength())) + uuid16, err := utils.NewUUID(int(config.UUIDLength)) if err != nil { h.log.Error("Failed to generate UUID", slog.String("error", err.Error())) @@ -127,7 +127,7 @@ func (h *HandlerV1) Handle(w http.ResponseWriter, r *http.Request) { resultTbl.ForEach(func(key lua.LValue, value lua.LValue) { out[key.String()] = utils.ConvertLuaTypesToGolang(value) }) - uuid32, _ := corestate.GetNodeUUID(filepath.Join(config.GetInternalConsts().GetMetaDir(), "uuid")) + uuid32, _ := corestate.GetNodeUUID(filepath.Join(config.MetaDir, "uuid")) response := ResponseFormat{ ResponsibleAgentUUID: uuid32, RequestedCommand: cmd, diff --git a/core/sv1/handle_list.go b/core/sv1/handle_list.go index 35a78ec..af81db0 100644 --- a/core/sv1/handle_list.go +++ b/core/sv1/handle_list.go @@ -16,7 +16,7 @@ import ( // The function processes the HTTP request and returns a list of available commands. func (h *HandlerV1) HandleList(w http.ResponseWriter, r *http.Request) { - uuid16, err := utils.NewUUID(int(config.GetInternalConsts().GetUUIDLength())) + uuid16, err := utils.NewUUID(int(config.UUIDLength)) if err != nil { h.log.Error("Failed to generate UUID", slog.String("error", err.Error())) @@ -111,7 +111,7 @@ func (h *HandlerV1) HandleList(w http.ResponseWriter, r *http.Request) { log.Debug("Command list prepared") log.Info("Session completed") - uuid32, _ := corestate.GetNodeUUID(filepath.Join(config.GetInternalConsts().GetMetaDir(), "uuid")) + uuid32, _ := corestate.GetNodeUUID(filepath.Join(config.MetaDir, "uuid")) response := ResponseFormat{ ResponsibleAgentUUID: uuid32, RequestedCommand: "list", diff --git a/core/update/update.go b/core/update/update.go index b072fa3..dbda3b0 100644 --- a/core/update/update.go +++ b/core/update/update.go @@ -6,16 +6,18 @@ import ( "errors" "fmt" "io" - "log/slog" + "log" "net/http" "os" - "os/exec" "path/filepath" - "regexp" "strconv" "strings" + "syscall" "github.com/akyaiy/GoSally-mvp/core/config" + "github.com/akyaiy/GoSally-mvp/core/run_manager" + "github.com/akyaiy/GoSally-mvp/core/utils" + "golang.org/x/net/context" ) const ( @@ -36,14 +38,20 @@ type UpdaterContract interface { } type Updater struct { - Log slog.Logger - Config *config.Conf + log *log.Logger + config *config.Conf + env *config.Env + + ctx context.Context + cancel context.CancelFunc } -func NewUpdater(log slog.Logger, cfg *config.Conf) *Updater { +func NewUpdater(ctx context.Context, log *log.Logger, cfg *config.Conf, env *config.Env) *Updater { return &Updater{ - Log: log, - Config: cfg, + log: log, + config: cfg, + env: env, + ctx: ctx, } } @@ -80,11 +88,11 @@ func isVersionNewer(current, latest Version) bool { if i < len(currentParts) { cur, err := strconv.Atoi(currentParts[i]) if err != nil { - cur = 0 // или можно обработать ошибку иначе + cur = 0 } curPart = cur } else { - curPart = 0 // Если части в current меньше, считаем недостающие нулями + curPart = 0 } if i < len(latestParts) { @@ -103,31 +111,15 @@ func isVersionNewer(current, latest Version) bool { if curPart > latPart { return false } - // если равны — идём дальше } - return false // все части равны, значит не новее + return false } -// if len(currentParts) >= 1 && len(latestParts) >= 1 { -// if currentParts[0] < latestParts[0] { -// if len(currentParts) < 2 || len(latestParts) < 2 { -// if currentParts[1] < latestParts[1] { -// return true -// } -// if currentParts[1] > latestParts[1] { -// return false -// } -// } -// if currentParts[0] > latestParts[0] { -// return false -// } -// } - // GetCurrentVersion reads the current version from the version file and returns it along with the branch. func (u *Updater) GetCurrentVersion() (Version, Branch, error) { - version, branch, err := splitVersionString(string(config.GetUpdateConsts().GetNodeVersion())) + version, branch, err := splitVersionString(string(config.NodeVersion)) if err != nil { - u.Log.Error("Failed to parse version string", slog.String("version", string(config.GetUpdateConsts().GetNodeVersion())), slog.String("error", err.Error())) + u.log.Printf("Failed to parse version string: %s", err.Error()) return "", "", err } switch branch { @@ -139,28 +131,28 @@ func (u *Updater) GetCurrentVersion() (Version, Branch, error) { } func (u *Updater) GetLatestVersion(updateBranch Branch) (Version, Branch, error) { - repoURL := u.Config.Updates.RepositoryURL + repoURL := u.config.Updates.RepositoryURL if repoURL == "" { - u.Log.Error("RepositoryURL is empty in config") + u.log.Printf("Failed to get latest version: %s", "RepositoryURL is empty in config") return "", "", errors.New("repository URL is empty") } if !strings.HasPrefix(repoURL, "http://") && !strings.HasPrefix(repoURL, "https://") { - u.Log.Error("RepositoryURL does not start with http:// or https://", slog.String("RepositoryURL", repoURL)) + u.log.Printf("Failed to get latest version: %s: %s", "RepositoryURL does not start with http:// or https:/", repoURL) return "", "", errors.New("repository URL must start with http:// or https://") } - response, err := http.Get(repoURL + "/" + config.GetUpdateConsts().GetActualFileName()) + response, err := http.Get(repoURL + "/" + config.ActualFileName) if err != nil { - u.Log.Error("Failed to fetch latest version", slog.String("error", err.Error())) + u.log.Printf("Failed to fetch latest version: %s", err.Error()) return "", "", err } defer response.Body.Close() if response.StatusCode != http.StatusOK { - u.Log.Error("Failed to fetch latest version", slog.Int("status", response.StatusCode)) + u.log.Printf("Failed to fetch latest version: HTTP status %d", response.StatusCode) return "", "", errors.New("failed to fetch latest version, status code: " + http.StatusText(response.StatusCode)) } data, err := io.ReadAll(response.Body) if err != nil { - u.Log.Error("Failed to read latest version response", slog.String("error", err.Error())) + u.log.Printf("Failed to read latest version response: %s", err.Error()) return "", "", err } lines := strings.Split(string(data), "\n") @@ -171,14 +163,13 @@ func (u *Updater) GetLatestVersion(updateBranch Branch) (Version, Branch, error) } version, branch, err := splitVersionString(string(line)) if err != nil { - u.Log.Error("Failed to parse version string", slog.String("version", string(line)), slog.String("error", err.Error())) + u.log.Printf("Failed to parse version string: %s", err.Error()) return "", "", err } if branch == updateBranch { return Version(version), Branch(branch), nil } } - u.Log.Warn("No version found for branch", slog.String("branch", string(updateBranch))) return "", "", errors.New("no version found for branch: " + string(updateBranch)) } @@ -198,146 +189,145 @@ func (u *Updater) CkeckUpdates() (IsNewUpdate, error) { } func (u *Updater) Update() error { - if !(u.Config.Updates.UpdatesEnabled) { + if !u.config.Updates.UpdatesEnabled { return errors.New("updates are disabled in config, skipping update") } - downloadPath, err := os.MkdirTemp("", "*-gosally-update") - if err != nil { - return errors.New("failed to create temp dir " + err.Error()) + + if err := run_manager.SetDir("update"); err != nil { + return fmt.Errorf("failed to create update dir: %w", err) } + + downloadPath := filepath.Join(run_manager.RuntimeDir(), "update") + _, currentBranch, err := u.GetCurrentVersion() if err != nil { - return errors.New("failed to get current version: " + err.Error()) + return fmt.Errorf("failed to get current version: %w", err) } latestVersion, latestBranch, err := u.GetLatestVersion(currentBranch) if err != nil { - return errors.New("failed to get latest version: " + err.Error()) + return fmt.Errorf("failed to get latest version: %w", err) } - updateArchiveName := config.GetUpdateConsts().GetUpdateArchiveName() + ".v" + string(latestVersion) + "-" + string(latestBranch) - updateDest := u.Config.Updates.RepositoryURL + "/" + updateArchiveName + ".tar.gz" + + updateArchiveName := fmt.Sprintf("%s.v%s-%s", config.UpdateArchiveName, latestVersion, latestBranch) + updateDest := fmt.Sprintf("%s/%s.%s", u.config.Updates.RepositoryURL, updateArchiveName, "tar.gz") + resp, err := http.Get(updateDest) if err != nil { - return errors.New("failed to fetch latest version archive: " + err.Error()) + return fmt.Errorf("failed to fetch archive: %w", err) } defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) - return errors.New("failed to fetch latest version archive: status " + resp.Status + ", body: " + string(body)) + return fmt.Errorf("unexpected HTTP status: %s, body: %s", resp.Status, body) } + gzReader, err := gzip.NewReader(resp.Body) if err != nil { - return errors.New("failed to create gzip reader: " + err.Error()) + return fmt.Errorf("gzip reader error: %w", err) } defer gzReader.Close() + tarReader := tar.NewReader(gzReader) for { header, err := tarReader.Next() if err == io.EOF { - break // archive is fully read + break } if err != nil { - return errors.New("failed to read tar header: " + err.Error()) + return fmt.Errorf("tar read error: %w", err) } - targetPath := filepath.Join(downloadPath, header.Name) + relativeParts := strings.SplitN(header.Name, string(os.PathSeparator), 2) + if len(relativeParts) < 2 { + // It's either a top level directory or garbage. + continue + } + cleanName := relativeParts[1] + targetPath := filepath.Join(downloadPath, cleanName) switch header.Typeflag { case tar.TypeDir: - // Создаём директорию if err := os.MkdirAll(targetPath, os.FileMode(header.Mode)); err != nil { - return errors.New("failed to create directory: " + err.Error()) + return fmt.Errorf("mkdir error: %w", err) } case tar.TypeReg: - // Создаём директорию, если её ещё нет - if err := os.MkdirAll(filepath.Dir(targetPath), 0755); err != nil { - return errors.New("failed to create directory for file: " + err.Error()) + if err := run_manager.Set(filepath.Join("update", cleanName)); err != nil { + return fmt.Errorf("set file error: %w", err) } - // Создаём файл - outFile, err := os.Create(targetPath) + f := run_manager.File(filepath.Join("update", cleanName)) + outFile, err := f.Open() if err != nil { - return errors.New("failed to create file: " + err.Error()) + return fmt.Errorf("open file error: %w", err) } - // Копируем содержимое if _, err := io.Copy(outFile, tarReader); err != nil { outFile.Close() - return errors.New("failed to copy file content: " + err.Error()) + return fmt.Errorf("copy file error: %w", err) } outFile.Close() default: - return errors.New("unsupported tar entry type: " + string(header.Typeflag)) + return fmt.Errorf("unsupported tar type: %v", header.Typeflag) } } - return u.InstallAndRestart(filepath.Join(downloadPath, updateArchiveName, "node")) + + return u.InstallAndRestart() } -func (u *Updater) InstallAndRestart(newBinaryPath string) error { - nodePath := os.Getenv("NODE_PATH") +func (u *Updater) InstallAndRestart() error { + + nodePath := u.env.NodePath if nodePath == "" { - return errors.New("NODE_PATH environment variable is not set") + return errors.New("GS_NODE_PATH environment variable is not set") } installDir := filepath.Join(nodePath, "bin") targetPath := filepath.Join(installDir, "node") - // Копируем новый бинарник - input, err := os.Open(newBinaryPath) + f := run_manager.File("update/node") + input, err := f.Open() if err != nil { - return err + return fmt.Errorf("cannot open new binary: %w", err) } + defer f.Close() output, err := os.Create(targetPath) if err != nil { - return err + return fmt.Errorf("cannot create target binary: %w", err) } - if _, err := io.Copy(output, input); err != nil { - return err - } - - if err := os.Chmod(targetPath, 0755); err != nil { - return errors.New("failed to chmod file: " + err.Error()) - } - - input.Close() - - reSafeTmpDir := regexp.QuoteMeta(os.TempDir()) - toClean := regexp.MustCompile( - fmt.Sprintf(`^(%s/\d+-gosally-update/)`, reSafeTmpDir), - ).FindStringSubmatch(newBinaryPath) - if len(toClean) > 1 { - os.RemoveAll(toClean[0]) + output.Close() + return fmt.Errorf("copy failed: %w", err) } output.Close() - // Запускаем новый процесс - u.Log.Info("Launching new version...", slog.String("path", targetPath)) - cmd := exec.Command(targetPath, os.Args[1:]...) - cmd.Env = os.Environ() - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err = nil - if err = cmd.Start(); err != nil { - return err + if err := os.Chmod(targetPath, 0755); err != nil { + return fmt.Errorf("failed to chmod: %w", err) } - u.Log.Info("Shutting down") - os.Exit(0) - return errors.New("failed to shutdown the process") + + u.log.Printf("Launching new version: path is %s", targetPath) + // cmd := exec.Command(targetPath, os.Args[1:]...) + // cmd.Env = os.Environ() + // cmd.Stdout = os.Stdout + // cmd.Stderr = os.Stderr + // cmd.Stdin = os.Stdin + args := os.Args + args[0] = targetPath + env := utils.SetEviron(os.Environ(), "GS_PARENT_PID=-1") + + run_manager.Clean() + return syscall.Exec(targetPath, args, env) + //u.cancel() + + // TODO: fix this crap and find a better way to update without errors + // for { + // _, err := run_manager.Get("run.lock") + // if err != nil { + // break + // } + // } + + // return cmd.Start() } -// func (u *Updater) Update() error { -// if !(u.Config.UpdatesEnabled && u.Config.Updates.AllowUpdates && u.Config.Updates.AllowDowngrades) { -// u.Log.Info("Updates are disabled in config, skipping update") -// return nil -// } -// wantedVersion := u.Config.Updates.WantedVersion -// _, wantedBranch, _ := splitVersionString(wantedVersion) -// newVersion, newBranch, err := u.GetLatestVersion(wantedBranch) -// if err != nil { -// return err -// } -// if wantedBranch != newBranch { -// u.Log.Info("Wanted version branch does not match latest version branch: updating wanted branch", -// slog.String("wanted_branch", string(wantedBranch)), -// slog.String("latest_branch", string(newBranch)), -// ) -// } -// } +func (u *Updater) Shutdownfunc(f context.CancelFunc) { + u.cancel = f +} diff --git a/core/utils/routines.go b/core/utils/routines.go index 4ef6984..787f598 100644 --- a/core/utils/routines.go +++ b/core/utils/routines.go @@ -1,11 +1,34 @@ package utils import ( + "fmt" "os" "path/filepath" "reflect" + "strings" ) +func SetEviron(eviron []string, envs ...string) []string { + envMap := make(map[string]string) + for _, e := range eviron { + parts := strings.SplitN(e, "=", 2) + if len(parts) == 2 { + envMap[parts[0]] = parts[1] + } + } + for _, e := range envs { + parts := strings.SplitN(e, "=", 2) + if len(parts) == 2 { + envMap[parts[0]] = parts[1] + } + } + newEviron := make([]string, 0, len(envMap)) + for k, v := range envMap { + newEviron = append(newEviron, fmt.Sprintf("%s=%s", k, v)) + } + + return newEviron +} func CleanTempRuntimes(pattern string) error { matches, err := filepath.Glob(pattern) if err != nil { @@ -42,7 +65,7 @@ func ExistsMatchingDirs(pattern, exclude string) (bool, error) { return false, nil } -func IndexPaths(runDir string) (*map[string]string, error) { +func IndexPaths(runDir string) (map[string]string, error) { indexed := make(map[string]string) err := filepath.Walk(runDir, func(path string, info os.FileInfo, err error) error { @@ -67,7 +90,7 @@ func IndexPaths(runDir string) (*map[string]string, error) { return nil, err } - return &indexed, nil + return indexed, nil } func IsFullyInitialized(i any) bool { diff --git a/core/utils/utils_test.go b/core/utils/utils_test.go new file mode 100644 index 0000000..870119f --- /dev/null +++ b/core/utils/utils_test.go @@ -0,0 +1,48 @@ +package utils + +import ( + "fmt" + "reflect" + "sort" + "testing" +) + +func TestFunc_SetEviron(t *testing.T) { + tests := []struct { + eviron []string + envs []string + want []string + }{ + { + []string{"ENV1=1", "ENV2=2", "ENV3=4"}, + []string{"ENV3=3"}, + []string{"ENV1=1", "ENV2=2", "ENV3=3"}, + }, + { + []string{"ENV1=1", "ENV2=5", "ENV3=4"}, + []string{"ENV2=2", "ENV3=3"}, + []string{"ENV1=1", "ENV2=2", "ENV3=3"}, + }, + { + []string{"ENV1=1", "ENV2=2", "ENV3=3"}, + []string{"ENV4=4"}, + []string{"ENV1=1", "ENV2=2", "ENV3=3", "ENV4=4"}, + }, + { + []string{"ENV1=1", "ENV2=2", "ENV3=4"}, + []string{"ENV3=2", "ENV3=3"}, + []string{"ENV1=1", "ENV2=2", "ENV3=3"}, + }, + } + + for _, tt := range tests { + t.Run(fmt.Sprintf("in %q set new %q", tt.eviron, tt.envs), func(t *testing.T) { + got := SetEviron(tt.eviron, tt.envs...) + sort.Strings(got) + sort.Strings(tt.want) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("SetEviron(%q, %q) = got %v; want %v", tt.eviron, tt.envs, got, tt.want) + } + }) + } +} diff --git a/core/utils/uuid.go b/core/utils/uuid.go index e8805fc..b125d02 100644 --- a/core/utils/uuid.go +++ b/core/utils/uuid.go @@ -26,15 +26,15 @@ func NewUUID(length int) (string, error) { } func NewUUID32() (string, error) { - return NewUUID(config.GetInternalConsts().GetUUIDLength()) + return NewUUID(config.UUIDLength) } func NewUUID32Raw() ([]byte, error) { - data, err := NewUUIDRaw(config.GetInternalConsts().GetUUIDLength()) + data, err := NewUUIDRaw(config.UUIDLength) if err != nil { return data, err } - if len(data) != config.GetInternalConsts().GetUUIDLength() { + if len(data) != config.UUIDLength { return data, errors.New("unexpected UUID length") } return data, nil diff --git a/main.go b/main.go index a6eebf9..09ec169 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,8 @@ package main -import "github.com/akyaiy/GoSally-mvp/cmd" +import ( + "github.com/akyaiy/GoSally-mvp/cmd" +) func main() { cmd.Execute()