mirror of
https://github.com/akyaiy/GoSally-mvp.git
synced 2026-01-03 07:12:26 +00:00
Fixing updates
This commit is contained in:
17
.vscode/launch.json
vendored
Normal file
17
.vscode/launch.json
vendored
Normal file
@@ -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": {},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -16,19 +16,18 @@ The basic directory tree looks something like this
|
|||||||
│ ├── echo.lua
|
│ ├── echo.lua
|
||||||
│ ├── _globals.lua Declaring global variables and functions for all internal scripts (also required for luarc to work correctly)
|
│ ├── _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
|
│ └── _prepare.lua Script that is executed before each script launch
|
||||||
├── config.yaml
|
└── config.yaml
|
||||||
└── Makefile
|
|
||||||
|
|
||||||
3 directories, 6 files
|
3 directories, 6 files
|
||||||
|
|
||||||
```
|
```
|
||||||
Launch by command
|
Launch by command
|
||||||
```bash
|
```bash
|
||||||
./node run
|
./bin/node run
|
||||||
```
|
```
|
||||||
or for structured logs
|
or for structured logs
|
||||||
```bash
|
```bash
|
||||||
./node run | jq
|
./bin/node run | jq
|
||||||
```
|
```
|
||||||
|
|
||||||
Example of GET request to server
|
Example of GET request to server
|
||||||
|
|||||||
16
cmd/root.go
16
cmd/root.go
@@ -1,12 +1,18 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"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"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var compositor *config.Compositor = config.NewCompositor()
|
||||||
|
|
||||||
var rootCmd = &cobra.Command{
|
var rootCmd = &cobra.Command{
|
||||||
Use: "node",
|
Use: "node",
|
||||||
Short: "Go Sally node",
|
Short: "Go Sally node",
|
||||||
@@ -18,9 +24,11 @@ var rootCmd = &cobra.Command{
|
|||||||
|
|
||||||
func Execute() {
|
func Execute() {
|
||||||
log.SetOutput(os.Stdout)
|
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)
|
log.SetFlags(log.Ldate | log.Ltime)
|
||||||
if err := rootCmd.Execute(); err != nil {
|
compositor.LoadCMDLine(rootCmd)
|
||||||
log.Fatalf("Unexpected error: %s", err.Error())
|
rootCmd.Execute()
|
||||||
}
|
// if err := rootCmd.Execute(); err != nil {
|
||||||
|
// log.Fatalf("Unexpected error: %s", err.Error())
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
176
cmd/run.go
176
cmd/run.go
@@ -13,7 +13,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -22,7 +21,9 @@ import (
|
|||||||
"github.com/akyaiy/GoSally-mvp/core/corestate"
|
"github.com/akyaiy/GoSally-mvp/core/corestate"
|
||||||
gs "github.com/akyaiy/GoSally-mvp/core/general_server"
|
gs "github.com/akyaiy/GoSally-mvp/core/general_server"
|
||||||
"github.com/akyaiy/GoSally-mvp/core/logs"
|
"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/sv1"
|
||||||
|
"github.com/akyaiy/GoSally-mvp/core/update"
|
||||||
"github.com/akyaiy/GoSally-mvp/core/utils"
|
"github.com/akyaiy/GoSally-mvp/core/utils"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/cors"
|
"github.com/go-chi/cors"
|
||||||
@@ -39,6 +40,7 @@ var runCmd = &cobra.Command{
|
|||||||
|
|
||||||
nodeApp.InitialHooks(
|
nodeApp.InitialHooks(
|
||||||
func(cs *corestate.CoreState, x *app.AppX) {
|
func(cs *corestate.CoreState, x *app.AppX) {
|
||||||
|
x.Config = compositor
|
||||||
x.Log.SetOutput(os.Stdout)
|
x.Log.SetOutput(os.Stdout)
|
||||||
x.Log.SetPrefix(logs.SetBrightBlack(fmt.Sprintf("(%s) ", cs.Stage)))
|
x.Log.SetPrefix(logs.SetBrightBlack(fmt.Sprintf("(%s) ", cs.Stage)))
|
||||||
x.Log.SetFlags(log.Ldate | log.Ltime)
|
x.Log.SetFlags(log.Ldate | log.Ltime)
|
||||||
@@ -49,29 +51,27 @@ var runCmd = &cobra.Command{
|
|||||||
*cs = *corestate.NewCorestate(&corestate.CoreState{
|
*cs = *corestate.NewCorestate(&corestate.CoreState{
|
||||||
UUID32DirName: "uuid",
|
UUID32DirName: "uuid",
|
||||||
NodeBinName: filepath.Base(os.Args[0]),
|
NodeBinName: filepath.Base(os.Args[0]),
|
||||||
NodeVersion: config.GetUpdateConsts().GetNodeVersion(),
|
NodeVersion: config.NodeVersion,
|
||||||
MetaDir: "./.meta",
|
MetaDir: "./.meta",
|
||||||
Stage: corestate.StagePreInit,
|
Stage: corestate.StagePreInit,
|
||||||
RM: corestate.NewRM(),
|
|
||||||
StartTimestampUnix: time.Now().Unix(),
|
StartTimestampUnix: time.Now().Unix(),
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
func(cs *corestate.CoreState, x *app.AppX) {
|
func(cs *corestate.CoreState, x *app.AppX) {
|
||||||
x.Log.SetPrefix(logs.SetBlue(fmt.Sprintf("(%s) ", cs.Stage)))
|
x.Log.SetPrefix(logs.SetBlue(fmt.Sprintf("(%s) ", cs.Stage)))
|
||||||
x.Config = config.NewCompositor()
|
|
||||||
if err := x.Config.LoadEnv(); err != nil {
|
if err := x.Config.LoadEnv(); err != nil {
|
||||||
x.Log.Fatalf("env load error: %s", err)
|
x.Log.Fatalf("env load error: %s", err)
|
||||||
}
|
}
|
||||||
cs.NodePath = x.Config.Env.NodePath
|
cs.NodePath = x.Config.Env.NodePath
|
||||||
|
|
||||||
if cfgPath := config.ConfigPath; cfgPath != "" {
|
if cfgPath := x.Config.CMDLine.Run.ConfigPath; cfgPath != "" {
|
||||||
x.Config.Env.ConfigPath = cfgPath
|
x.Config.Env.ConfigPath = cfgPath
|
||||||
}
|
}
|
||||||
if err := x.Config.LoadConf(x.Config.Env.ConfigPath); err != nil {
|
if err := x.Config.LoadConf(x.Config.Env.ConfigPath); err != nil {
|
||||||
x.Log.Fatalf("conf load error: %s", err)
|
x.Log.Fatalf("conf load error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
func(cs *corestate.CoreState, x *app.AppX) {
|
func(cs *corestate.CoreState, x *app.AppX) {
|
||||||
@@ -92,63 +92,54 @@ var runCmd = &cobra.Command{
|
|||||||
},
|
},
|
||||||
|
|
||||||
func(cs *corestate.CoreState, x *app.AppX) {
|
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
|
// still pre-init stage
|
||||||
func(cs *corestate.CoreState, x *app.AppX) {
|
runDir, err := run_manager.Create(cs.UUID32)
|
||||||
runDir, err := cs.RM.Create(cs.UUID32)
|
if err != nil {
|
||||||
if err != nil {
|
x.Log.Fatalf("Unexpected failure: %s", err.Error())
|
||||||
x.Log.Fatalf("Unexpected failure: %s", err.Error())
|
}
|
||||||
}
|
cs.RunDir = runDir
|
||||||
cs.RunDir = runDir
|
input, err := os.Open(os.Args[0])
|
||||||
input, err := os.Open(os.Args[0])
|
if err != nil {
|
||||||
if err != nil {
|
run_manager.Clean()
|
||||||
cs.RM.Clean()
|
x.Log.Fatalf("Unexpected failure: %s", err.Error())
|
||||||
x.Log.Fatalf("Unexpected failure: %s", err.Error())
|
}
|
||||||
}
|
if err := run_manager.Set(cs.NodeBinName); err != nil {
|
||||||
if err := cs.RM.Set(cs.NodeBinName); err != nil {
|
run_manager.Clean()
|
||||||
cs.RM.Clean()
|
x.Log.Fatalf("Unexpected failure: %s", err.Error())
|
||||||
x.Log.Fatalf("Unexpected failure: %s", err.Error())
|
}
|
||||||
}
|
fmgr := run_manager.File(cs.NodeBinName)
|
||||||
fmgr := cs.RM.File(cs.NodeBinName)
|
output, err := fmgr.Open()
|
||||||
output, err := fmgr.Open()
|
if err != nil {
|
||||||
if err != nil {
|
run_manager.Clean()
|
||||||
cs.RM.Clean()
|
x.Log.Fatalf("Unexpected failure: %s", err.Error())
|
||||||
x.Log.Fatalf("Unexpected failure: %s", err.Error())
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := io.Copy(output, input); err != nil {
|
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()
|
|
||||||
fmgr.Close()
|
fmgr.Close()
|
||||||
runArgs := os.Args
|
run_manager.Clean()
|
||||||
runArgs[0] = filepath.Join(cs.RunDir, cs.NodeBinName)
|
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
|
// prepare environ
|
||||||
env := os.Environ()
|
env := utils.SetEviron(os.Environ(), fmt.Sprintf("GS_PARENT_PID=%d", os.Getpid()))
|
||||||
|
|
||||||
var filtered []string
|
if err := syscall.Exec(runArgs[0], runArgs, env); err != nil {
|
||||||
for _, e := range env {
|
run_manager.Clean()
|
||||||
if strings.HasPrefix(e, "GS_PARENT_PID=") {
|
x.Log.Fatalf("Unexpected failure: %s", err.Error())
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
x.Log.Printf("Node uuid is %s", cs.UUID32)
|
x.Log.Printf("Node uuid is %s", cs.UUID32)
|
||||||
},
|
},
|
||||||
@@ -158,30 +149,30 @@ var runCmd = &cobra.Command{
|
|||||||
cs.Stage = corestate.StagePostInit
|
cs.Stage = corestate.StagePostInit
|
||||||
x.Log.SetPrefix(logs.SetYellow(fmt.Sprintf("(%s) ", cs.Stage)))
|
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)
|
exist, err := utils.ExistsMatchingDirs(filepath.Join(os.TempDir(), fmt.Sprintf("/*-%s-%s", cs.UUID32, "gosally-runtime")), cs.RunDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cs.RM.Clean()
|
run_manager.Clean()
|
||||||
x.Log.Fatalf("Unexpected failure: %s", err.Error())
|
x.Log.Fatalf("Unexpected failure: %s", err.Error())
|
||||||
}
|
}
|
||||||
if exist {
|
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")
|
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 {
|
if err := run_manager.Set("run.lock"); err != nil {
|
||||||
cs.RM.Clean()
|
run_manager.Clean()
|
||||||
x.Log.Fatalf("Unexpected failure: %s", err.Error())
|
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 {
|
if err != nil {
|
||||||
cs.RM.Clean()
|
run_manager.Clean()
|
||||||
x.Log.Fatalf("Unexpected failure: %s", err.Error())
|
x.Log.Fatalf("Unexpected failure: %s", err.Error())
|
||||||
}
|
}
|
||||||
lockFile := ini.Empty()
|
lockFile := ini.Empty()
|
||||||
secRun, err := lockFile.NewSection("runtime")
|
secRun, err := lockFile.NewSection("runtime")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cs.RM.Clean()
|
run_manager.Clean()
|
||||||
x.Log.Fatalf("Unexpected failure: %s", err.Error())
|
x.Log.Fatalf("Unexpected failure: %s", err.Error())
|
||||||
}
|
}
|
||||||
secRun.Key("pid").SetValue(fmt.Sprintf("%d/%d", os.Getpid(), x.Config.Env.ParentStagePID))
|
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)
|
err = lockFile.SaveTo(lockPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cs.RM.Clean()
|
run_manager.Clean()
|
||||||
x.Log.Fatalf("Unexpected failure: %s", err.Error())
|
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 {
|
nodeApp.Run(func(ctx context.Context, cs *corestate.CoreState, x *app.AppX) error {
|
||||||
ctxMain, cancelMain := context.WithCancel(ctx)
|
ctxMain, cancelMain := context.WithCancel(ctx)
|
||||||
runLockFile := cs.RM.File("run.lock")
|
runLockFile := run_manager.File("run.lock")
|
||||||
_, err := runLockFile.Open()
|
_, err := runLockFile.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
x.Log.Fatalf("cannot open run.lock: %s", err)
|
x.Log.Fatalf("cannot open run.lock: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
_, err = runLockFile.Watch(ctxMain, func() {
|
||||||
err := runLockFile.Watch(ctxMain, func() {
|
x.Log.Printf("run.lock was touched")
|
||||||
x.Log.Printf("run.lock was touched")
|
run_manager.Clean()
|
||||||
cs.RM.Clean()
|
cancelMain()
|
||||||
cancelMain()
|
})
|
||||||
})
|
if err != nil {
|
||||||
if err != nil {
|
x.Log.Printf("watch error: %s", err)
|
||||||
x.Log.Printf("watch error: %s", err)
|
}
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
serverv1 := sv1.InitV1Server(&sv1.HandlerV1InitStruct{
|
serverv1 := sv1.InitV1Server(&sv1.HandlerV1InitStruct{
|
||||||
Log: *x.SLog,
|
Log: *x.SLog,
|
||||||
@@ -246,7 +235,7 @@ var runCmd = &cobra.Command{
|
|||||||
AllowCredentials: true,
|
AllowCredentials: true,
|
||||||
MaxAge: 300,
|
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("/", s.HandleList)
|
||||||
r.Get("/{cmd}", s.Handle)
|
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()))
|
x.SLog.Error("Failed to start HTTP server", slog.String("error", err.Error()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
srv.Shutdown(ctxMain)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if err := srv.Shutdown(ctxMain); err != nil {
|
if x.Config.Conf.Updates.UpdatesEnabled {
|
||||||
x.Log.Printf("%s", fmt.Sprintf("Failed to shutdown server gracefully: %s", err.Error()))
|
go func() {
|
||||||
} else {
|
x.Updated = update.NewUpdater(ctxMain, x.Log, x.Config.Conf, x.Config.Env)
|
||||||
x.Log.Printf("The server shut down successfully")
|
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()
|
<-ctxMain.Done()
|
||||||
|
|
||||||
x.Log.Println("cleaning up...")
|
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.Printf("cleanup error: %s", err)
|
||||||
}
|
}
|
||||||
x.Log.Println("bye!")
|
x.Log.Println("bye!")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
runCmd.Flags().StringVarP(&config.ConfigPath, "config", "c", "./config.yaml", "Path to configuration file")
|
|
||||||
rootCmd.AddCommand(runCmd)
|
rootCmd.AddCommand(runCmd)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,6 @@
|
|||||||
--- #args
|
--- #args
|
||||||
--- msg = the message
|
--- msg = the message
|
||||||
|
|
||||||
local os = require("os")
|
|
||||||
|
|
||||||
os.execute("touch 1")
|
|
||||||
|
|
||||||
if not In.Params.msg or In.Params.msg == "" then
|
if not In.Params.msg or In.Params.msg == "" then
|
||||||
Out.Result.status = Status.error
|
Out.Result.status = Status.error
|
||||||
Out.Result.error = "Missing parameter: msg"
|
Out.Result.error = "Missing parameter: msg"
|
||||||
|
|||||||
@@ -16,6 +16,6 @@ tls:
|
|||||||
com_dir: "com/"
|
com_dir: "com/"
|
||||||
|
|
||||||
updates:
|
updates:
|
||||||
enabled: false
|
enabled: true
|
||||||
check-interval: 1h
|
check-interval: 1h
|
||||||
repository_url: "https://repo.serve.lv/raw/go-sally"
|
repository_url: "https://repo.serve.lv/raw/go-sally"
|
||||||
@@ -2,8 +2,11 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -52,12 +55,6 @@ func (c *Compositor) LoadConf(path string) error {
|
|||||||
v.SetDefault("updates.check_interval", "2h")
|
v.SetDefault("updates.check_interval", "2h")
|
||||||
v.SetDefault("updates.wanted_version", "latest-stable")
|
v.SetDefault("updates.wanted_version", "latest-stable")
|
||||||
|
|
||||||
// поддержка ENV-переопределений
|
|
||||||
v.SetEnvPrefix("GOSALLY")
|
|
||||||
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
|
||||||
v.AutomaticEnv()
|
|
||||||
|
|
||||||
// читаем YAML
|
|
||||||
if err := v.ReadInConfig(); err != nil {
|
if err := v.ReadInConfig(); err != nil {
|
||||||
return fmt.Errorf("error reading config: %w", err)
|
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)
|
return fmt.Errorf("error unmarshaling config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.Conf = &Conf{}
|
||||||
c.Conf = &cfg
|
c.Conf = &cfg
|
||||||
return nil
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,16 +6,15 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ConfigPath string
|
|
||||||
|
|
||||||
type CompositorContract interface {
|
type CompositorContract interface {
|
||||||
LoadEnv() error
|
LoadEnv() error
|
||||||
LoadConf(path string) error
|
LoadConf(path string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type Compositor struct {
|
type Compositor struct {
|
||||||
Conf *Conf
|
CMDLine *CMDLine
|
||||||
Env *Env
|
Conf *Conf
|
||||||
|
Env *Env
|
||||||
}
|
}
|
||||||
|
|
||||||
type Conf struct {
|
type Conf struct {
|
||||||
@@ -57,3 +56,17 @@ type Env struct {
|
|||||||
NodePath string `mapstructure:"node_path"`
|
NodePath string `mapstructure:"node_path"`
|
||||||
ParentStagePID int `mapstructure:"parent_pid"`
|
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"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,25 +27,8 @@ var UpdateDownloadPath string = os.TempDir()
|
|||||||
|
|
||||||
var MetaDir string = "./.meta"
|
var MetaDir string = "./.meta"
|
||||||
|
|
||||||
type _internalConsts struct{}
|
func init() {
|
||||||
type _serverConsts struct{}
|
|
||||||
type _updateConsts struct{}
|
|
||||||
|
|
||||||
func GetUpdateConsts() _updateConsts { return _updateConsts{} }
|
|
||||||
func (_ _updateConsts) GetNodeVersion() string {
|
|
||||||
if NodeVersion == "" {
|
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 }
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func readNodeUUIDRaw(p string) ([]byte, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return data, err
|
return data, err
|
||||||
}
|
}
|
||||||
if len(data) != config.GetInternalConsts().GetUUIDLength() {
|
if len(data) != config.UUIDLength {
|
||||||
return data, errors.New("decoded UUID length mismatch")
|
return data, errors.New("decoded UUID length mismatch")
|
||||||
}
|
}
|
||||||
return data, nil
|
return data, nil
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -1,16 +1,10 @@
|
|||||||
package corestate
|
package corestate
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CoreStateContract is interface for CoreState.
|
// CoreStateContract is interface for CoreState.
|
||||||
// CoreState is a structure that contains the basic meta-information vital to the node.
|
// 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,
|
// The interface contains functionality for working with the Runtime directory and its files,
|
||||||
// and access to low-level logging in stdout
|
// and access to low-level logging in stdout
|
||||||
type CoreStateContract interface {
|
type CoreStateContract interface {
|
||||||
RuntimeDir() RunManagerContract
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type CoreState struct {
|
type CoreState struct {
|
||||||
@@ -27,36 +21,4 @@ type CoreState struct {
|
|||||||
NodePath string
|
NodePath string
|
||||||
MetaDir string
|
MetaDir string
|
||||||
RunDir 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
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package corestate
|
package run_manager
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -8,16 +8,16 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *RunManager) File(index string) RunFileManagerContract {
|
func File(index string) RunFileManagerContract {
|
||||||
value, ok := (*r.indexedPaths)[index]
|
value, ok := indexedPaths[index]
|
||||||
if !ok {
|
if !ok {
|
||||||
err := r.indexPaths()
|
err := indexPaths()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &RunFileManager{
|
return &RunFileManager{
|
||||||
err: err,
|
err: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
value, ok = (*r.indexedPaths)[index]
|
value, ok = indexedPaths[index]
|
||||||
if !ok {
|
if !ok {
|
||||||
return &RunFileManager{
|
return &RunFileManager{
|
||||||
err: fmt.Errorf("cannot detect file under index %s", index),
|
err: fmt.Errorf("cannot detect file under index %s", index),
|
||||||
@@ -45,22 +45,24 @@ func (r *RunFileManager) Close() error {
|
|||||||
return r.file.Close()
|
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 {
|
if r.err != nil {
|
||||||
return r.err
|
return nil, r.err
|
||||||
}
|
}
|
||||||
if r.file == nil {
|
if r.file == nil {
|
||||||
return fmt.Errorf("file is not opened")
|
return nil, fmt.Errorf("file is not opened")
|
||||||
}
|
}
|
||||||
|
|
||||||
info, err := r.file.Stat()
|
info, err := r.file.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
origStat := info.Sys().(*syscall.Stat_t)
|
origStat := info.Sys().(*syscall.Stat_t)
|
||||||
origIno := origStat.Ino
|
origIno := origStat.Ino
|
||||||
origModTime := info.ModTime()
|
origModTime := info.ModTime()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(parentCtx)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@@ -89,5 +91,5 @@ func (r *RunFileManager) Watch(ctx context.Context, callback func()) error {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return cancel, nil
|
||||||
}
|
}
|
||||||
158
core/run_manager/run_manager.go
Normal file
158
core/run_manager/run_manager.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
// The function processes the HTTP request and runs Lua scripts,
|
// The function processes the HTTP request and runs Lua scripts,
|
||||||
// preparing the environment and subsequently transmitting the execution result
|
// preparing the environment and subsequently transmitting the execution result
|
||||||
func (h *HandlerV1) Handle(w http.ResponseWriter, r *http.Request) {
|
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 {
|
if err != nil {
|
||||||
h.log.Error("Failed to generate UUID",
|
h.log.Error("Failed to generate UUID",
|
||||||
slog.String("error", err.Error()))
|
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) {
|
resultTbl.ForEach(func(key lua.LValue, value lua.LValue) {
|
||||||
out[key.String()] = utils.ConvertLuaTypesToGolang(value)
|
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{
|
response := ResponseFormat{
|
||||||
ResponsibleAgentUUID: uuid32,
|
ResponsibleAgentUUID: uuid32,
|
||||||
RequestedCommand: cmd,
|
RequestedCommand: cmd,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import (
|
|||||||
|
|
||||||
// The function processes the HTTP request and returns a list of available commands.
|
// The function processes the HTTP request and returns a list of available commands.
|
||||||
func (h *HandlerV1) HandleList(w http.ResponseWriter, r *http.Request) {
|
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 {
|
if err != nil {
|
||||||
h.log.Error("Failed to generate UUID",
|
h.log.Error("Failed to generate UUID",
|
||||||
slog.String("error", err.Error()))
|
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.Debug("Command list prepared")
|
||||||
|
|
||||||
log.Info("Session completed")
|
log.Info("Session completed")
|
||||||
uuid32, _ := corestate.GetNodeUUID(filepath.Join(config.GetInternalConsts().GetMetaDir(), "uuid"))
|
uuid32, _ := corestate.GetNodeUUID(filepath.Join(config.MetaDir, "uuid"))
|
||||||
response := ResponseFormat{
|
response := ResponseFormat{
|
||||||
ResponsibleAgentUUID: uuid32,
|
ResponsibleAgentUUID: uuid32,
|
||||||
RequestedCommand: "list",
|
RequestedCommand: "list",
|
||||||
|
|||||||
@@ -6,16 +6,18 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log/slog"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/akyaiy/GoSally-mvp/core/config"
|
"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 (
|
const (
|
||||||
@@ -36,14 +38,20 @@ type UpdaterContract interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Updater struct {
|
type Updater struct {
|
||||||
Log slog.Logger
|
log *log.Logger
|
||||||
Config *config.Conf
|
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{
|
return &Updater{
|
||||||
Log: log,
|
log: log,
|
||||||
Config: cfg,
|
config: cfg,
|
||||||
|
env: env,
|
||||||
|
ctx: ctx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,11 +88,11 @@ func isVersionNewer(current, latest Version) bool {
|
|||||||
if i < len(currentParts) {
|
if i < len(currentParts) {
|
||||||
cur, err := strconv.Atoi(currentParts[i])
|
cur, err := strconv.Atoi(currentParts[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cur = 0 // или можно обработать ошибку иначе
|
cur = 0
|
||||||
}
|
}
|
||||||
curPart = cur
|
curPart = cur
|
||||||
} else {
|
} else {
|
||||||
curPart = 0 // Если части в current меньше, считаем недостающие нулями
|
curPart = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if i < len(latestParts) {
|
if i < len(latestParts) {
|
||||||
@@ -103,31 +111,15 @@ func isVersionNewer(current, latest Version) bool {
|
|||||||
if curPart > latPart {
|
if curPart > latPart {
|
||||||
return false
|
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.
|
// GetCurrentVersion reads the current version from the version file and returns it along with the branch.
|
||||||
func (u *Updater) GetCurrentVersion() (Version, Branch, error) {
|
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 {
|
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
|
return "", "", err
|
||||||
}
|
}
|
||||||
switch branch {
|
switch branch {
|
||||||
@@ -139,28 +131,28 @@ func (u *Updater) GetCurrentVersion() (Version, Branch, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *Updater) GetLatestVersion(updateBranch Branch) (Version, Branch, error) {
|
func (u *Updater) GetLatestVersion(updateBranch Branch) (Version, Branch, error) {
|
||||||
repoURL := u.Config.Updates.RepositoryURL
|
repoURL := u.config.Updates.RepositoryURL
|
||||||
if repoURL == "" {
|
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")
|
return "", "", errors.New("repository URL is empty")
|
||||||
}
|
}
|
||||||
if !strings.HasPrefix(repoURL, "http://") && !strings.HasPrefix(repoURL, "https://") {
|
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://")
|
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 {
|
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
|
return "", "", err
|
||||||
}
|
}
|
||||||
defer response.Body.Close()
|
defer response.Body.Close()
|
||||||
if response.StatusCode != http.StatusOK {
|
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))
|
return "", "", errors.New("failed to fetch latest version, status code: " + http.StatusText(response.StatusCode))
|
||||||
}
|
}
|
||||||
data, err := io.ReadAll(response.Body)
|
data, err := io.ReadAll(response.Body)
|
||||||
if err != nil {
|
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
|
return "", "", err
|
||||||
}
|
}
|
||||||
lines := strings.Split(string(data), "\n")
|
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))
|
version, branch, err := splitVersionString(string(line))
|
||||||
if err != nil {
|
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
|
return "", "", err
|
||||||
}
|
}
|
||||||
if branch == updateBranch {
|
if branch == updateBranch {
|
||||||
return Version(version), Branch(branch), nil
|
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))
|
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 {
|
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")
|
return errors.New("updates are disabled in config, skipping update")
|
||||||
}
|
}
|
||||||
downloadPath, err := os.MkdirTemp("", "*-gosally-update")
|
|
||||||
if err != nil {
|
if err := run_manager.SetDir("update"); err != nil {
|
||||||
return errors.New("failed to create temp dir " + err.Error())
|
return fmt.Errorf("failed to create update dir: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
downloadPath := filepath.Join(run_manager.RuntimeDir(), "update")
|
||||||
|
|
||||||
_, currentBranch, err := u.GetCurrentVersion()
|
_, currentBranch, err := u.GetCurrentVersion()
|
||||||
if err != nil {
|
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)
|
latestVersion, latestBranch, err := u.GetLatestVersion(currentBranch)
|
||||||
if err != nil {
|
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)
|
resp, err := http.Get(updateDest)
|
||||||
if err != nil {
|
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()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
body, _ := io.ReadAll(resp.Body)
|
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)
|
gzReader, err := gzip.NewReader(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("failed to create gzip reader: " + err.Error())
|
return fmt.Errorf("gzip reader error: %w", err)
|
||||||
}
|
}
|
||||||
defer gzReader.Close()
|
defer gzReader.Close()
|
||||||
|
|
||||||
tarReader := tar.NewReader(gzReader)
|
tarReader := tar.NewReader(gzReader)
|
||||||
for {
|
for {
|
||||||
header, err := tarReader.Next()
|
header, err := tarReader.Next()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
break // archive is fully read
|
break
|
||||||
}
|
}
|
||||||
if err != nil {
|
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 {
|
switch header.Typeflag {
|
||||||
case tar.TypeDir:
|
case tar.TypeDir:
|
||||||
// Создаём директорию
|
|
||||||
if err := os.MkdirAll(targetPath, os.FileMode(header.Mode)); err != nil {
|
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:
|
case tar.TypeReg:
|
||||||
// Создаём директорию, если её ещё нет
|
if err := run_manager.Set(filepath.Join("update", cleanName)); err != nil {
|
||||||
if err := os.MkdirAll(filepath.Dir(targetPath), 0755); err != nil {
|
return fmt.Errorf("set file error: %w", err)
|
||||||
return errors.New("failed to create directory for file: " + err.Error())
|
|
||||||
}
|
}
|
||||||
// Создаём файл
|
f := run_manager.File(filepath.Join("update", cleanName))
|
||||||
outFile, err := os.Create(targetPath)
|
outFile, err := f.Open()
|
||||||
if err != nil {
|
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 {
|
if _, err := io.Copy(outFile, tarReader); err != nil {
|
||||||
outFile.Close()
|
outFile.Close()
|
||||||
return errors.New("failed to copy file content: " + err.Error())
|
return fmt.Errorf("copy file error: %w", err)
|
||||||
}
|
}
|
||||||
outFile.Close()
|
outFile.Close()
|
||||||
default:
|
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 {
|
func (u *Updater) InstallAndRestart() error {
|
||||||
nodePath := os.Getenv("NODE_PATH")
|
|
||||||
|
nodePath := u.env.NodePath
|
||||||
if 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")
|
installDir := filepath.Join(nodePath, "bin")
|
||||||
targetPath := filepath.Join(installDir, "node")
|
targetPath := filepath.Join(installDir, "node")
|
||||||
|
|
||||||
// Копируем новый бинарник
|
f := run_manager.File("update/node")
|
||||||
input, err := os.Open(newBinaryPath)
|
input, err := f.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("cannot open new binary: %w", err)
|
||||||
}
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
output, err := os.Create(targetPath)
|
output, err := os.Create(targetPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("cannot create target binary: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := io.Copy(output, input); err != nil {
|
if _, err := io.Copy(output, input); err != nil {
|
||||||
return err
|
output.Close()
|
||||||
}
|
return fmt.Errorf("copy failed: %w", 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()
|
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 := os.Chmod(targetPath, 0755); err != nil {
|
||||||
if err = cmd.Start(); err != nil {
|
return fmt.Errorf("failed to chmod: %w", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
u.Log.Info("Shutting down")
|
|
||||||
os.Exit(0)
|
u.log.Printf("Launching new version: path is %s", targetPath)
|
||||||
return errors.New("failed to shutdown the process")
|
// 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 {
|
func (u *Updater) Shutdownfunc(f context.CancelFunc) {
|
||||||
// if !(u.Config.UpdatesEnabled && u.Config.Updates.AllowUpdates && u.Config.Updates.AllowDowngrades) {
|
u.cancel = f
|
||||||
// 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)),
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -1,11 +1,34 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"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 {
|
func CleanTempRuntimes(pattern string) error {
|
||||||
matches, err := filepath.Glob(pattern)
|
matches, err := filepath.Glob(pattern)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -42,7 +65,7 @@ func ExistsMatchingDirs(pattern, exclude string) (bool, error) {
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func IndexPaths(runDir string) (*map[string]string, error) {
|
func IndexPaths(runDir string) (map[string]string, error) {
|
||||||
indexed := make(map[string]string)
|
indexed := make(map[string]string)
|
||||||
|
|
||||||
err := filepath.Walk(runDir, func(path string, info os.FileInfo, err error) error {
|
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 nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &indexed, nil
|
return indexed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsFullyInitialized(i any) bool {
|
func IsFullyInitialized(i any) bool {
|
||||||
|
|||||||
48
core/utils/utils_test.go
Normal file
48
core/utils/utils_test.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,15 +26,15 @@ func NewUUID(length int) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewUUID32() (string, error) {
|
func NewUUID32() (string, error) {
|
||||||
return NewUUID(config.GetInternalConsts().GetUUIDLength())
|
return NewUUID(config.UUIDLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUUID32Raw() ([]byte, error) {
|
func NewUUID32Raw() ([]byte, error) {
|
||||||
data, err := NewUUIDRaw(config.GetInternalConsts().GetUUIDLength())
|
data, err := NewUUIDRaw(config.UUIDLength)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return data, err
|
return data, err
|
||||||
}
|
}
|
||||||
if len(data) != config.GetInternalConsts().GetUUIDLength() {
|
if len(data) != config.UUIDLength {
|
||||||
return data, errors.New("unexpected UUID length")
|
return data, errors.New("unexpected UUID length")
|
||||||
}
|
}
|
||||||
return data, nil
|
return data, nil
|
||||||
|
|||||||
Reference in New Issue
Block a user