From 66e83f1e3bafc71accdabcd1949e807d72037b1b Mon Sep 17 00:00:00 2001 From: et-nik Date: Wed, 21 Feb 2024 21:19:29 +0100 Subject: [PATCH] windows fixes and changes --- .../components/extendable_executor_test.go | 3 +- internal/app/config/config.go | 12 +- internal/app/config/loader.go | 2 + .../app/di/internal/definitions/services.go | 9 +- .../delete_server_test.go | 4 +- internal/processmanager/loader_darwin.go | 4 + internal/processmanager/loader_linux.go | 4 + internal/processmanager/loader_windows.go | 6 +- internal/processmanager/systemd.go | 7 +- internal/processmanager/winsw.go | 244 ++++++++++++++++-- .../gdtasks/commands/cmdexec_test.go | 4 +- .../functional/gdtasks/commands/start_test.go | 7 +- .../functional/gdtasks/commands/suite_test.go | 3 +- .../serverscommand/install/install_test.go | 21 +- .../functional/servertest/files/mkdir_test.go | 9 +- .../servertest/files/move_copy_test.go | 28 +- .../functional/servertest/files/suite_test.go | 5 +- 17 files changed, 306 insertions(+), 66 deletions(-) diff --git a/internal/app/components/extendable_executor_test.go b/internal/app/components/extendable_executor_test.go index 7135ad6..3cc54db 100644 --- a/internal/app/components/extendable_executor_test.go +++ b/internal/app/components/extendable_executor_test.go @@ -3,6 +3,7 @@ package components_test import ( "context" "os" + "path/filepath" "runtime" "syscall" "testing" @@ -37,7 +38,7 @@ func TestExtendableExecutor_ExecGetTool_ExpectToolDownloaded(t *testing.T) { require.NoError(t, err) require.Equal(t, 0, code) require.NotEmpty(t, result) - path := tmpDir + "/fastdl.sh" + path := filepath.Join(tmpDir, "/fastdl.sh") assert.FileExists(t, path) assertFileIsExecutableByOwner(t, path) } diff --git a/internal/app/config/config.go b/internal/app/config/config.go index 25a9e9c..e90b172 100644 --- a/internal/app/config/config.go +++ b/internal/app/config/config.go @@ -2,7 +2,10 @@ package config import ( "os" + "path/filepath" "time" + + "github.com/gameap/daemon/internal/processmanager" ) type Scripts struct { @@ -73,6 +76,11 @@ type Config struct { RunTaskPeriod time.Duration `yaml:"run_task_period"` WorkersCount int `yaml:"workers_count"` } `yaml:"task_manager"` + + ProcessManager struct { + Name string `yaml:"name"` + Config map[string]string `yaml:"config"` + } `yaml:"process_manager"` } func NewConfig() *Config { @@ -86,7 +94,7 @@ func NewConfig() *Config { func (cfg *Config) Init() error { if cfg.ToolsPath == "" { - cfg.ToolsPath = cfg.WorkPath + "/tools" + cfg.ToolsPath = filepath.Join(cfg.WorkPath, "tools") } if cfg.TaskManager.UpdatePeriod == 0 { @@ -97,6 +105,8 @@ func (cfg *Config) Init() error { cfg.TaskManager.RunTaskPeriod = 10 * time.Millisecond } + cfg.ProcessManager.Name = processmanager.Default + return cfg.validate() } diff --git a/internal/app/config/loader.go b/internal/app/config/loader.go index ecf7c82..623100b 100644 --- a/internal/app/config/loader.go +++ b/internal/app/config/loader.go @@ -97,6 +97,8 @@ func loadIni(path string) (*Config, error) { cfg.PrivateKeyPassword = c.Section("").Key("private_key_password").String() cfg.DHFile = c.Section("").Key("dh_file").String() + cfg.ProcessManager.Name = c.Section("").Key("process_manager").String() + cfg.LogLevel = c.Section("").Key("log_level").MustString("debug") cfg.OutputLog = c.Section("").Key("output_log").MustString("") diff --git a/internal/app/di/internal/definitions/services.go b/internal/app/di/internal/definitions/services.go index 451c56a..aab3e12 100644 --- a/internal/app/di/internal/definitions/services.go +++ b/internal/app/di/internal/definitions/services.go @@ -76,10 +76,17 @@ func CreateServiceExtendableExecutor(ctx context.Context, c Container) contracts } func CreateServicesProcessManager(ctx context.Context, c Container) contracts.ProcessManager { - return processmanager.NewSystemD( + pm, err := processmanager.Load( + c.Cfg(ctx).ProcessManager.Name, c.Cfg(ctx), c.Services().Executor(ctx), ) + if err != nil { + c.SetError(err) + return nil + } + + return pm } func CreateServicesGdTaskManager(ctx context.Context, c Container) *gdaemonscheduler.TaskManager { diff --git a/internal/app/game_server_commands/delete_server_test.go b/internal/app/game_server_commands/delete_server_test.go index 720d303..b68e63f 100644 --- a/internal/app/game_server_commands/delete_server_test.go +++ b/internal/app/game_server_commands/delete_server_test.go @@ -98,8 +98,8 @@ func (suite *deleteSuite) TestDeleteServerByScriptSuccess() { suite.Require().Nil(err) suite.Assert().Equal(SuccessResult, deleteServerCommand.Result()) suite.Assert().NoFileExists(server.WorkDir(cfg)) - suite.Assert().NoFileExists(server.WorkDir(cfg) + "/" + "run.sh") - suite.Assert().NoFileExists(server.WorkDir(cfg) + "/" + "run2.sh") + suite.Assert().NoFileExists(filepath.Join(server.WorkDir(cfg), "run.sh")) + suite.Assert().NoFileExists(filepath.Join(server.WorkDir(cfg), "run2.sh")) } func (suite *deleteSuite) TestDeleteServerByScript_CommandFail() { diff --git a/internal/processmanager/loader_darwin.go b/internal/processmanager/loader_darwin.go index 37be46b..279c055 100644 --- a/internal/processmanager/loader_darwin.go +++ b/internal/processmanager/loader_darwin.go @@ -8,6 +8,10 @@ import ( "github.com/gameap/daemon/internal/app/contracts" ) +const ( + Default = "tmux" +) + func Load(name string, cfg *config.Config, executor contracts.Executor) (contracts.ProcessManager, error) { switch name { case "tmux": diff --git a/internal/processmanager/loader_linux.go b/internal/processmanager/loader_linux.go index e2da8e7..da84dbd 100644 --- a/internal/processmanager/loader_linux.go +++ b/internal/processmanager/loader_linux.go @@ -8,6 +8,10 @@ import ( "github.com/gameap/daemon/internal/app/contracts" ) +const ( + Default = "tmux" +) + func Load(name string, cfg *config.Config, executor contracts.Executor) (contracts.ProcessManager, error) { switch name { case "tmux": diff --git a/internal/processmanager/loader_windows.go b/internal/processmanager/loader_windows.go index ceb5f01..6390b46 100644 --- a/internal/processmanager/loader_windows.go +++ b/internal/processmanager/loader_windows.go @@ -8,10 +8,14 @@ import ( "github.com/gameap/daemon/internal/app/contracts" ) +const ( + Default = "winsw" +) + func Load(name string, cfg *config.Config, executor contracts.Executor) (contracts.ProcessManager, error) { switch name { case "winsw": - return NewWindowsService(cfg, executor), nil + return NewWinSW(cfg, executor), nil case "simple": return NewSimple(cfg, executor), nil default: diff --git a/internal/processmanager/systemd.go b/internal/processmanager/systemd.go index 9b04d4a..acd3df6 100644 --- a/internal/processmanager/systemd.go +++ b/internal/processmanager/systemd.go @@ -22,6 +22,7 @@ import ( const ( systemdFilesDir = ".systemd-services" systemdServicesDir = "/etc/systemd/system" + servicePrefix = "gameap-server-" // https://www.freedesktop.org/software/systemd/man/latest/systemctl.html#Exit%20status statusIsDeadPidExists = 1 @@ -388,7 +389,7 @@ func (pm *SystemD) buildSocketConfig(server *domain.Server) string { builder.WriteString("\n") builder.WriteString("Service=") - builder.WriteString("gameap-") + builder.WriteString(servicePrefix) builder.WriteString(server.UUID()) builder.WriteString(".service\n") @@ -427,7 +428,7 @@ func (pm *SystemD) serviceName(server *domain.Server) string { builder := strings.Builder{} builder.Grow(50) - builder.WriteString("gameap-") + builder.WriteString(servicePrefix) builder.WriteString(server.UUID()) builder.WriteString(".service") @@ -442,7 +443,7 @@ func (pm *SystemD) socketName(server *domain.Server) string { builder := strings.Builder{} builder.Grow(50) - builder.WriteString("gameap-") + builder.WriteString(servicePrefix) builder.WriteString(server.UUID()) builder.WriteString(".socket") diff --git a/internal/processmanager/winsw.go b/internal/processmanager/winsw.go index e2cf163..dd75631 100644 --- a/internal/processmanager/winsw.go +++ b/internal/processmanager/winsw.go @@ -5,74 +5,270 @@ package processmanager import ( "context" + "encoding/xml" + "fmt" "io" + "os" + "path/filepath" + "strings" "github.com/gameap/daemon/internal/app/config" "github.com/gameap/daemon/internal/app/contracts" "github.com/gameap/daemon/internal/app/domain" + "github.com/gameap/daemon/pkg/logger" + "github.com/gopherclass/go-shellquote" + "github.com/pkg/errors" ) const ( servicesConfigPath = "C:\\gameap\\services" + servicePrefix = "gameap-server-" + + outputSizeLimit = 10000 ) -type WindowsService struct { +type WinSW struct { cfg *config.Config executor contracts.Executor } -func NewWindowsService(cfg *config.Config, executor contracts.Executor) *WindowsService { - return &WindowsService{ +func NewWinSW(cfg *config.Config, executor contracts.Executor) *WinSW { + return &WinSW{ cfg: cfg, executor: executor, } } -func (ws *WindowsService) Start(ctx context.Context, server *domain.Server, out io.Writer) (domain.Result, error) { - //TODO implement me - panic("implement me") +func (pm *WinSW) Start(ctx context.Context, server *domain.Server, out io.Writer) (domain.Result, error) { + return pm.command(ctx, server, "start", out) +} + +func (pm *WinSW) Stop(ctx context.Context, server *domain.Server, out io.Writer) (domain.Result, error) { + result, err := pm.runWinSWCommand(ctx, "stop", server) + if err != nil { + return domain.ErrorResult, errors.WithMessage(err, "failed to exec command") + } + + err = os.Remove(pm.serviceFile(server)) + if err != nil { + logger.WithError(ctx, err).Warn("failed to remove service file") + } + + return result, nil } -func (ws *WindowsService) Stop(ctx context.Context, server *domain.Server, out io.Writer) (domain.Result, error) { - //TODO implement me - panic("implement me") +func (pm *WinSW) Restart(ctx context.Context, server *domain.Server, out io.Writer) (domain.Result, error) { + return pm.command(ctx, server, "restart", out) } -func (ws *WindowsService) Restart(ctx context.Context, server *domain.Server, out io.Writer) (domain.Result, error) { - //TODO implement me - panic("implement me") +func (pm *WinSW) Status(ctx context.Context, server *domain.Server, out io.Writer) (domain.Result, error) { + return pm.command(ctx, server, "status", out) } -func (ws *WindowsService) Status(ctx context.Context, server *domain.Server, out io.Writer) (domain.Result, error) { - //TODO implement me - panic("implement me") +func (pm *WinSW) runWinSWCommand(ctx context.Context, command string, server *domain.Server) (domain.Result, error) { + _, result, err := pm.executor.Exec( + ctx, + fmt.Sprintf("winsw %s %s ", command, pm.serviceFile(server)), + contracts.ExecutorOptions{ + WorkDir: pm.cfg.WorkDir(), + }, + ) + return domain.Result(result), err } -func (ws *WindowsService) GetOutput(ctx context.Context, server *domain.Server, out io.Writer) (domain.Result, error) { - //TODO implement me - panic("implement me") +func (pm *WinSW) command( + ctx context.Context, server *domain.Server, command string, out io.Writer, +) (domain.Result, error) { + createdNewService, err := pm.makeService(ctx, server) + if err != nil { + return domain.ErrorResult, errors.WithMessage(err, "failed to make service") + } + + if createdNewService { + _, err = pm.runWinSWCommand(ctx, "install", server) + if err != nil { + return domain.ErrorResult, errors.WithMessage(err, "failed to install service") + } + } else { + _, err = pm.runWinSWCommand(ctx, "refresh", server) + if err != nil { + return domain.ErrorResult, errors.WithMessage(err, "failed to refresh service config") + } + } + + result, err := pm.runWinSWCommand(ctx, command, server) + if err != nil { + return domain.ErrorResult, errors.WithMessage(err, "failed to exec command") + } + + return result, nil } -func (ws *WindowsService) SendInput( +func (pm *WinSW) GetOutput(ctx context.Context, server *domain.Server, out io.Writer) (domain.Result, error) { + f, err := os.Open(pm.logPath(server)) + if err != nil { + return domain.ErrorResult, errors.WithMessage(err, "failed to open file") + } + + defer func() { + err := f.Close() + if err != nil { + logger.Warn(ctx, errors.WithMessage(err, "failed to close file")) + } + }() + + stat, err := f.Stat() + if err != nil { + return domain.ErrorResult, errors.WithMessage(err, "failed to get file stat") + } + + if stat.Size() > outputSizeLimit { + _, err = f.Seek(-outputSizeLimit, io.SeekEnd) + if err != nil { + return domain.ErrorResult, errors.WithMessage(err, "failed to seek file") + } + } + + _, err = io.Copy(out, f) + if err != nil { + return domain.ErrorResult, errors.WithMessage(err, "failed to copy file") + } + + return domain.SuccessResult, nil +} + +func (pm *WinSW) SendInput( ctx context.Context, input string, server *domain.Server, out io.Writer, ) (domain.Result, error) { - //TODO implement me - panic("implement me") + return domain.ErrorResult, errors.New("input is not supported") +} + +func (pm *WinSW) makeService(ctx context.Context, server *domain.Server) (bool, error) { + serviceFile := pm.serviceFile(server) + + if _, err := os.Stat(servicesConfigPath); errors.Is(err, os.ErrNotExist) { + err := os.MkdirAll(servicesConfigPath, 0755) + if err != nil { + return false, errors.WithMessage(err, "failed to create directory") + } + } + + createdNew := false + if _, err := os.Stat(serviceFile); errors.Is(err, os.ErrNotExist) { + // It means that service file does not exist. + // We will create new service. + // If file exists, we will update it. + createdNew = true + } + + f, err := os.OpenFile(serviceFile, os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return false, errors.WithMessage(err, "failed to open file") + } + defer func() { + err := f.Close() + if err != nil { + logger.Warn(ctx, errors.WithMessage(err, "failed to close file")) + } + }() + + c, err := pm.buildServiceConfig(server) + if err != nil { + return false, errors.WithMessage(err, "failed to build service config") + } + + _, err = f.WriteString(c) + if err != nil { + return false, errors.WithMessage(err, "failed to write to file") + } + + return createdNew, nil +} + +func (pm *WinSW) buildServiceConfig(server *domain.Server) (string, error) { + cmd := domain.MakeFullCommand( + pm.cfg, + server, + pm.cfg.Scripts.Start, + server.StartCommand(), + ) + + cmdArr, err := shellquote.Split(cmd) + if err != nil { + return "", errors.WithMessage(err, "failed to split command") + } + + executable := cmdArr[0] + arguments := strings.Join(cmdArr[1:], " ") + + executable = filepath.Join(server.WorkDir(pm.cfg), executable) + + serviceName := pm.serviceName(server) + serviceConfig := WinSWServiceConfig{ + ID: serviceName, + Name: serviceName, + Executable: executable, + Arguments: arguments, + WorkingDirectory: server.WorkDir(pm.cfg), + Logpath: pm.logPath(server), + Log: log{ + Mode: "reset", + }, + OnFailure: []onFailure{ + {Action: "restart", Delay: "1 sec"}, + {Action: "restart", Delay: "2 sec"}, + {Action: "restart", Delay: "5 sec"}, + {Action: "restart", Delay: "5 sec"}, + }, + ResetFailure: "1 hour", + } + + out, err := xml.Marshal(struct { + WinSWServiceConfig + XMLName struct{} `xml:"service"` + }{WinSWServiceConfig: serviceConfig}) + if err != nil { + return "", errors.WithMessage(err, "failed to marshal xml") + } + + return string(out), nil +} + +func (pm *WinSW) serviceName(server *domain.Server) string { + builder := strings.Builder{} + builder.Grow(50) + + builder.WriteString(servicePrefix) + builder.WriteString(server.UUID()) + + return builder.String() +} + +func (pm *WinSW) serviceFile(server *domain.Server) string { + return filepath.Join(servicesConfigPath, server.UUID()+".xml") +} + +func (pm *WinSW) logPath(server *domain.Server) string { + return filepath.Join(servicesConfigPath, server.UUID()+".log") } type WinSWServiceConfig struct { ID string `xml:"id"` Name string `xml:"name"` Executable string `xml:"executable"` - WorkingDirectory string `xml:"workingdirectory,omitempty"` Arguments string `xml:"arguments,omitempty"` + WorkingDirectory string `xml:"workingdirectory,omitempty"` StopExecutable string `xml:"stopexecutable,omitempty"` StopArguments string `xml:"stoparguments,omitempty"` + StopTimeout string `xml:"stoptimeout,omitempty"` OnFailure []onFailure `xml:"onfailure,omitempty"` ResetFailure string `xml:"resetfailure,omitempty"` + Logpath string `xml:"logpath,omitempty"` + Log log `xml:"log,omitempty"` + ServiceAccount struct { Username string `xml:"username,omitempty"` Password string `xml:"password,omitempty"` @@ -83,3 +279,7 @@ type onFailure struct { Action string `xml:"action,attr"` Delay string `xml:"delay,attr,omitempty"` } + +type log struct { + Mode string `xml:"mode,attr"` +} diff --git a/test/functional/gdtasks/commands/cmdexec_test.go b/test/functional/gdtasks/commands/cmdexec_test.go index 23b07d3..d132851 100644 --- a/test/functional/gdtasks/commands/cmdexec_test.go +++ b/test/functional/gdtasks/commands/cmdexec_test.go @@ -1,6 +1,8 @@ package commands import ( + "path/filepath" + "github.com/gameap/daemon/internal/app/domain" ) @@ -37,5 +39,5 @@ func (suite *Suite) TestExecuteGetToolSuccess() { domain.GDTaskStatusSuccess, ), ) - suite.Assert().FileExists(suite.Cfg.ToolsPath + "/fastdl.sh") + suite.Assert().FileExists(filepath.Join(suite.Cfg.ToolsPath, "/fastdl.sh")) } diff --git a/test/functional/gdtasks/commands/start_test.go b/test/functional/gdtasks/commands/start_test.go index 9b5c750..2c42952 100644 --- a/test/functional/gdtasks/commands/start_test.go +++ b/test/functional/gdtasks/commands/start_test.go @@ -1,6 +1,7 @@ package commands import ( + "path/filepath" "runtime" "github.com/gameap/daemon/internal/app/domain" @@ -32,7 +33,7 @@ func (suite *Suite) TestStartScriptReturnFailError() { suite.RunTaskManagerUntilTasksCompleted([]*domain.GDTask{task}) - suite.Assert().FileExists(suite.WorkPath + "/server/fail_executed.txt") + suite.Assert().FileExists(filepath.Join(suite.WorkPath, "server", "fail_executed.txt")) suite.AssertGDTaskExist( domain.NewGDTask( 1, @@ -88,6 +89,6 @@ func (suite *Suite) TestRaceTasks() { suite.RunTaskManagerUntilTasksCompleted(tasks) - suite.FileExists(suite.WorkPath + "/server/sleep_and_check.txt") - suite.NoFileExists(suite.WorkPath + "/server/sleep_and_check_fail.txt") + suite.FileExists(filepath.Join(suite.WorkPath, "server", "sleep_ and_check_fail.txt")) + suite.NoFileExists(filepath.Join(suite.WorkPath, "server", "sleep_and_check_fail.txt")) } diff --git a/test/functional/gdtasks/commands/suite_test.go b/test/functional/gdtasks/commands/suite_test.go index df66de7..d220005 100644 --- a/test/functional/gdtasks/commands/suite_test.go +++ b/test/functional/gdtasks/commands/suite_test.go @@ -2,6 +2,7 @@ package commands import ( "os" + "path/filepath" "testing" "github.com/gameap/daemon/test/functional/gdtasks" @@ -38,7 +39,7 @@ func (suite *Suite) SetupTest() { } suite.Cfg.WorkPath = suite.WorkPath - suite.Cfg.ToolsPath = suite.WorkPath + "/tools" + suite.Cfg.ToolsPath = filepath.Join(suite.WorkPath, "tools") } func (suite *Suite) TearDownTest() { diff --git a/test/functional/serverscommand/install/install_test.go b/test/functional/serverscommand/install/install_test.go index 527d806..5216c3e 100644 --- a/test/functional/serverscommand/install/install_test.go +++ b/test/functional/serverscommand/install/install_test.go @@ -2,6 +2,7 @@ package install import ( "context" + "path/filepath" "github.com/gameap/daemon/internal/app/domain" gameservercommands "github.com/gameap/daemon/internal/app/game_server_commands" @@ -36,8 +37,8 @@ func (suite *Suite) TestInstall_InstallFromRemoteRepository_GameInstalled() { err := cmd.Execute(context.Background(), server) suite.Require().NoError(err) - suite.FileExists(suite.WorkPath + "/server/run.sh") //nolint:goconst - suite.NoFileExists(suite.WorkPath + "/server/.gamemodinstalled") //nolint:goconst + suite.FileExists(filepath.Join(suite.WorkPath, "server", "run.sh")) + suite.NoFileExists(filepath.Join(suite.WorkPath, "server", ".gamemodinstalled")) } func (suite *Suite) TestInstall_InstallFromRemoteRepository_GameAndModInstalled() { @@ -57,8 +58,8 @@ func (suite *Suite) TestInstall_InstallFromRemoteRepository_GameAndModInstalled( err := cmd.Execute(context.Background(), server) suite.Require().NoError(err) - suite.FileExists(suite.WorkPath + "/server/run.sh") - suite.FileExists(suite.WorkPath + "/server/.gamemodinstalled") + suite.FileExists(filepath.Join(suite.WorkPath, "server", "run.sh")) + suite.FileExists(filepath.Join(suite.WorkPath, "server", ".gamemodinstalled")) } func (suite *Suite) TestInstall_InstallFromLocalRepository_GameAndModInstalledFromLocalRepository() { @@ -66,12 +67,12 @@ func (suite *Suite) TestInstall_InstallFromLocalRepository_GameAndModInstalledFr domain.Game{ StartCode: "cstrike", RemoteRepository: "https://files.gameap.ru/test/test.tar.xz", - LocalRepository: suite.WorkPath + "/repository/game.tar.gz", + LocalRepository: filepath.Join(suite.WorkPath, "repository", "game.tar.gz"), }, domain.GameMod{ Name: "public", RemoteRepository: "https://files.gameap.ru/mod-game.tar.gz", - LocalRepository: suite.WorkPath + "/repository/game_mod.tar.gz", + LocalRepository: filepath.Join(suite.WorkPath, "repository", "game_mod.tar.gz"), }, ) server.SetInstallationStatus(domain.ServerNotInstalled) @@ -80,8 +81,8 @@ func (suite *Suite) TestInstall_InstallFromLocalRepository_GameAndModInstalledFr err := cmd.Execute(context.Background(), server) suite.Require().NoError(err) - suite.FileExists(suite.WorkPath + "/server/game_file_from_tar_gz") - suite.FileExists(suite.WorkPath + "/server/game_mod_file_from_tar_gz") - suite.NoFileExists(suite.WorkPath + "/server/run.sh") - suite.NoFileExists(suite.WorkPath + "/server/.gamemodinstalled") + suite.FileExists(filepath.Join(suite.WorkPath, "server", "game_file_from_tar_gz")) + suite.FileExists(filepath.Join(suite.WorkPath, "server", "game_mod_file_from_tar_gz")) + suite.NoFileExists(filepath.Join(suite.WorkPath, "server", "run.sh")) + suite.NoFileExists(filepath.Join(suite.WorkPath, "server", ".gamemodinstalled")) } diff --git a/test/functional/servertest/files/mkdir_test.go b/test/functional/servertest/files/mkdir_test.go index 8d66e8e..6afb72f 100644 --- a/test/functional/servertest/files/mkdir_test.go +++ b/test/functional/servertest/files/mkdir_test.go @@ -2,6 +2,7 @@ package files import ( "os" + "path/filepath" "strconv" "time" @@ -13,7 +14,7 @@ import ( func (suite *Suite) TestMakeDirSuccess() { suite.Auth(server.ModeFiles) - tempDir := os.TempDir() + "/files_test_" + strconv.Itoa(int(time.Now().UnixNano())) //nolint:goconst + tempDir := filepath.Join(os.TempDir(), "files_test_", strconv.Itoa(int(time.Now().UnixNano()))) //nolint:goconst defer os.RemoveAll(tempDir) msg := []interface{}{files.MakeDir, tempDir} @@ -25,9 +26,9 @@ func (suite *Suite) TestMakeDirSuccess() { func (suite *Suite) TestMakeDir_WhenThreeMessage_ExpectSuccess() { suite.Auth(server.ModeFiles) - tempDir1 := os.TempDir() + "/files_test_" + strconv.Itoa(int(time.Now().UnixNano())) - tempDir2 := os.TempDir() + "/files_test_" + strconv.Itoa(int(time.Now().UnixNano())) - tempDir3 := os.TempDir() + "/files_test_" + strconv.Itoa(int(time.Now().UnixNano())) + tempDir1 := filepath.Join(os.TempDir(), "files_test", strconv.Itoa(int(time.Now().UnixNano()))) + tempDir2 := filepath.Join(os.TempDir(), "files_test", strconv.Itoa(int(time.Now().UnixNano()))) + tempDir3 := filepath.Join(os.TempDir(), "files_test", strconv.Itoa(int(time.Now().UnixNano()))) defer os.RemoveAll(tempDir1) defer os.RemoveAll(tempDir2) defer os.RemoveAll(tempDir3) diff --git a/test/functional/servertest/files/move_copy_test.go b/test/functional/servertest/files/move_copy_test.go index 21a8831..a6e7f77 100644 --- a/test/functional/servertest/files/move_copy_test.go +++ b/test/functional/servertest/files/move_copy_test.go @@ -19,7 +19,7 @@ func (suite *Suite) TestMoveFileSuccess() { tempFile, _ := os.CreateTemp(tempDir, "file") tempFileName := tempFile.Name() _ = tempFile.Close() - newFile := tempDir + "/newFile" + newFile := filepath.Join(tempDir, "newFile") msg := []interface{}{files.FileMove, tempFileName, newFile, false} r := suite.ClientWriteReadAndDecodeList(msg) @@ -35,7 +35,7 @@ func (suite *Suite) TestCopyFileSuccess() { tempDir, _ := os.MkdirTemp("", "files_test_") defer os.RemoveAll(tempDir) tempFile, _ := os.CreateTemp(tempDir, "file") - newFile := tempDir + "/newFile" + newFile := filepath.Join(tempDir, "newFile") msg := []interface{}{files.FileMove, tempFile.Name(), newFile, true} r := suite.ClientWriteReadAndDecodeList(msg) @@ -48,17 +48,17 @@ func (suite *Suite) TestCopyFileSuccess() { func (suite *Suite) TestCopyRelativePathSuccess() { suite.Auth(server.ModeFiles) - tempDir := os.TempDir() + "/files_test_" + strconv.Itoa(int(time.Now().UnixNano())) + tempDir := filepath.Join(os.TempDir(), "files_test", strconv.Itoa(int(time.Now().UnixNano()))) msg := []interface{}{files.FileMove, "../../../../test/files", tempDir, true} r := suite.ClientWriteReadAndDecodeList(msg) suite.Equal(response.StatusOK, response.Code(r[0].(uint8))) - suite.DirExists(tempDir + "/directory") - suite.FileExists(tempDir + "/file.json") - suite.FileExists(tempDir + "/file.txt") - if runtime.GOOS != "windows" && suite.FileExists(tempDir+"/symlink_to_file_txt") { - s, err := os.Lstat(tempDir + "/symlink_to_file_txt") + suite.DirExists(filepath.Join(tempDir, "/directory")) + suite.FileExists(filepath.Join(tempDir, "/file.json")) + suite.FileExists(filepath.Join(tempDir, "/file.txt")) + if runtime.GOOS != "windows" && suite.FileExists(filepath.Join(tempDir, "symlink_to_file_txt")) { + s, err := os.Lstat(filepath.Join(tempDir, "symlink_to_file_txt")) if err != nil { suite.T().Fatal(err) } @@ -71,7 +71,7 @@ func (suite *Suite) TestCopyDirectorySuccess() { tempDirSource, _ := os.MkdirTemp("", "files_test_source_") defer os.RemoveAll(tempDirSource) //nolint:goconst - tempDirDestination := os.TempDir() + "/files_test_destination_" + strconv.Itoa(int(time.Now().UnixNano())) + tempDirDestination := filepath.Join(os.TempDir(), "files_test_destination", strconv.Itoa(int(time.Now().UnixNano()))) defer os.RemoveAll(tempDirDestination) tempFile, _ := os.CreateTemp(tempDirSource, "file") tempFile.Close() @@ -80,14 +80,14 @@ func (suite *Suite) TestCopyDirectorySuccess() { r := suite.ClientWriteReadAndDecodeList(msg) suite.Equal(response.StatusOK, response.Code(r[0].(uint8))) - suite.FileExists(tempDirSource + "/" + filepath.Base(tempFile.Name())) - suite.FileExists(tempDirDestination + "/" + filepath.Base(tempFile.Name())) + suite.FileExists(filepath.Join(tempDirDestination, filepath.Base(tempFile.Name()))) + suite.FileExists(filepath.Join(tempDirDestination, filepath.Base(tempFile.Name()))) } func (suite *Suite) TestMoveDirectorySuccess() { suite.Auth(server.ModeFiles) tempDirSource, _ := os.MkdirTemp("", "files_test_source_") - tempDirDestination := os.TempDir() + "/files_test_destination_" + strconv.Itoa(int(time.Now().UnixNano())) + tempDirDestination := filepath.Join(os.TempDir(), "files_test_destination", strconv.Itoa(int(time.Now().UnixNano()))) defer os.RemoveAll(tempDirDestination) tempFile, _ := os.CreateTemp(tempDirSource, "file") tempFileName := tempFile.Name() @@ -98,8 +98,8 @@ func (suite *Suite) TestMoveDirectorySuccess() { suite.Equal(response.StatusOK, response.Code(r[0].(uint8))) suite.NoDirExists(tempDirSource) - suite.NoFileExists(tempDirSource + "/" + filepath.Base(tempFileName)) - suite.FileExists(tempDirDestination + "/" + filepath.Base(tempFileName)) + suite.NoFileExists(filepath.Join(tempDirSource, filepath.Base(tempFileName))) + suite.FileExists(filepath.Join(tempDirDestination, filepath.Base(tempFileName))) } func (suite *Suite) TestMoveInvalidSource() { diff --git a/test/functional/servertest/files/suite_test.go b/test/functional/servertest/files/suite_test.go index 8d2474e..fe346d0 100644 --- a/test/functional/servertest/files/suite_test.go +++ b/test/functional/servertest/files/suite_test.go @@ -2,6 +2,7 @@ package files import ( "os" + "path/filepath" "runtime" "strconv" "testing" @@ -27,8 +28,8 @@ func TestSuite(t *testing.T) { func (suite *Suite) SetupTest() { suite.Suite.SetupTest() - tempDirDestination := os.TempDir() + "/files_test_destination_" + strconv.Itoa(int(time.Now().UnixNano())) - suite.tempFileDestination = tempDirDestination + "/file" + tempDirDestination := filepath.Join(os.TempDir(), "files_test_destination_", strconv.Itoa(int(time.Now().UnixNano()))) + suite.tempFileDestination = filepath.Join(tempDirDestination, "file") } func (suite *Suite) TearDownSuite() {