diff --git a/cmd/root.go b/cmd/root.go index 30859731c..443993ac4 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -9,6 +9,7 @@ import ( "github.com/spf13/cobra" + "github.com/stellar/kelp/gui/backend" "github.com/stellar/kelp/support/networking" "github.com/stellar/kelp/support/sdk" "github.com/stellar/kelp/support/utils" @@ -71,6 +72,7 @@ var rootCcxtRestURL *string func init() { validateBuild() + backend.SetVersionString(guiVersion, version) rootCcxtRestURL = RootCmd.PersistentFlags().String("ccxt-rest-url", "", "URL to use for the CCXT-rest API. Takes precendence over the CCXT_REST_URL param set in the botConfg file for the trade command and passed as a parameter into the Kelp subprocesses started by the GUI (default URL is https://localhost:3000)") diff --git a/cmd/server_amd64.go b/cmd/server_amd64.go index b79103a5f..cfcc4f7c5 100644 --- a/cmd/server_amd64.go +++ b/cmd/server_amd64.go @@ -126,7 +126,8 @@ func init() { uiLogsDirPath := kos.GetDotKelpWorkingDir().Join(uiLogsDir) log.Printf("calling mkdir on uiLogsDirPath: %s ...", uiLogsDirPath.AsString()) - e = kos.Mkdir(uiLogsDirPath) + // no need to pass a userID since we are not running under the context of any user at this point + e = kos.Mkdir("_", uiLogsDirPath) if e != nil { panic(errors.Wrap(e, "could not mkdir on uiLogsDirPath: "+uiLogsDirPath.AsString())) } @@ -293,29 +294,34 @@ func init() { ccxtBinPath := ccxtDestDir.Join(ccxtBinaryName) log.Printf("mkdir ccxtDirPath: %s ...", ccxtDirPath.AsString()) - e := kos.Mkdir(ccxtDirPath) + // no need to pass a userID since we are not running under the context of any user at this point + e := kos.Mkdir("_", ccxtDirPath) if e != nil { panic(fmt.Errorf("could not mkdir for ccxtDirPath: %s", e)) } if runtime.GOOS == "windows" { ccxtSourceDir := kos.GetBinDir().Join("ccxt").Join(ccxtFilenameNoExt) - e = copyCcxtFolder(kos, ccxtSourceDir, ccxtDestDir) + // no need to pass a userID since we are not running under the context of any user at this point + e = copyCcxtFolder(kos, "_", ccxtSourceDir, ccxtDestDir) if e != nil { panic(e) } } else { ccxtBundledZipPath := kos.GetBinDir().Join("ccxt").Join(filenameWithExt) ccxtZipDestPath := ccxtDirPath.Join(filenameWithExt) - e = copyOrDownloadCcxtBinary(kos, ccxtBundledZipPath, ccxtZipDestPath, filenameWithExt) + // no need to pass a userID since we are not running under the context of any user at this point + e = copyOrDownloadCcxtBinary(kos, "_", ccxtBundledZipPath, ccxtZipDestPath, filenameWithExt) if e != nil { panic(e) } - unzipCcxtFile(kos, ccxtDirPath, ccxtBinPath, filenameWithExt) + // no need to pass a userID since we are not running under the context of any user at this point + unzipCcxtFile(kos, "_", ccxtDirPath, ccxtBinPath, filenameWithExt) } - e = runCcxtBinary(kos, ccxtBinPath) + // no need to pass a userID since we are not running under the context of any user at this point + e = runCcxtBinary(kos, "_", ccxtBinPath) if e != nil { panic(e) } @@ -476,13 +482,14 @@ func setMiddleware(r *chi.Mux) { func copyCcxtFolder( kos *kelpos.KelpOS, + userID string, ccxtSourceDir *kelpos.OSPath, ccxtDestDir *kelpos.OSPath, ) error { log.Printf("copying ccxt directory from %s to location %s ...", ccxtSourceDir.AsString(), ccxtDestDir.AsString()) cpCmd := fmt.Sprintf("cp -a %s %s", ccxtSourceDir.Unix(), ccxtDestDir.Unix()) - _, e := kos.Blocking("cp-ccxt", cpCmd) + _, e := kos.Blocking(userID, "cp-ccxt", cpCmd) if e != nil { return fmt.Errorf("unable to copy ccxt directory from %s to %s: %s", ccxtSourceDir.AsString(), ccxtDestDir.AsString(), e) } @@ -493,6 +500,7 @@ func copyCcxtFolder( func copyOrDownloadCcxtBinary( kos *kelpos.KelpOS, + userID string, ccxtBundledZipPath *kelpos.OSPath, ccxtZipDestPath *kelpos.OSPath, filenameWithExt string, @@ -505,7 +513,7 @@ func copyOrDownloadCcxtBinary( log.Printf("copying ccxt from %s to location %s ...", ccxtBundledZipPath.Unix(), ccxtZipDestPath.Unix()) cpCmd := fmt.Sprintf("cp %s %s", ccxtBundledZipPath.Unix(), ccxtZipDestPath.Unix()) - _, e = kos.Blocking("cp-ccxt", cpCmd) + _, e = kos.Blocking(userID, "cp-ccxt", cpCmd) if e != nil { return fmt.Errorf("unable to copy ccxt zip file from %s to %s: %s", ccxtBundledZipPath.Unix(), ccxtZipDestPath.Unix(), e) } @@ -546,6 +554,7 @@ func copyOrDownloadCcxtBinary( func unzipCcxtFile( kos *kelpos.KelpOS, + userID string, ccxtDir *kelpos.OSPath, ccxtBinPath *kelpos.OSPath, filenameWithExt string, @@ -558,21 +567,21 @@ func unzipCcxtFile( log.Printf("unzipping file %s ... ", filenameWithExt) zipCmd := fmt.Sprintf("cd %s && unzip %s", ccxtDir.Unix(), filenameWithExt) - _, e := kos.Blocking("zip", zipCmd) + _, e := kos.Blocking(userID, "zip", zipCmd) if e != nil { log.Fatal(errors.Wrap(e, fmt.Sprintf("unable to unzip file %s in directory %s", filenameWithExt, ccxtDir.AsString()))) } log.Printf("done\n") } -func runCcxtBinary(kos *kelpos.KelpOS, ccxtBinPath *kelpos.OSPath) error { +func runCcxtBinary(kos *kelpos.KelpOS, userID string, ccxtBinPath *kelpos.OSPath) error { if _, e := os.Stat(ccxtBinPath.Native()); os.IsNotExist(e) { return fmt.Errorf("path to ccxt binary (%s) does not exist", ccxtBinPath.AsString()) } log.Printf("running binary %s", ccxtBinPath.AsString()) // TODO CCXT should be run at the port specified by rootCcxtRestURL, currently it will default to port 3000 even if the config file specifies otherwise - _, e := kos.Background("ccxt-rest", ccxtBinPath.Unix()) + _, e := kos.Background(userID, "ccxt-rest", ccxtBinPath.Unix()) if e != nil { log.Fatal(errors.Wrap(e, fmt.Sprintf("unable to run ccxt file at location %s", ccxtBinPath.AsString()))) } @@ -653,7 +662,8 @@ func writeTrayIcon(kos *kelpos.KelpOS, trayIconPath *kelpos.OSPath, assetsDirPat // create dir if not exists if _, e := os.Stat(assetsDirPath.Native()); os.IsNotExist(e) { log.Printf("mkdir assetsDirPath: %s ...", assetsDirPath.AsString()) - e = kos.Mkdir(assetsDirPath) + // no need to pass a userID since we are not running under the context of any user at this point + e = kos.Mkdir("_", assetsDirPath) if e != nil { return errors.Wrap(e, "could not mkdir for assetsDirPath: "+assetsDirPath.AsString()) } diff --git a/gui/backend/api_server.go b/gui/backend/api_server.go index 600d3dd7d..2200e31a2 100644 --- a/gui/backend/api_server.go +++ b/gui/backend/api_server.go @@ -281,33 +281,33 @@ func (s *APIServer) writeJsonWithLog(w http.ResponseWriter, v interface{}, doLog w.Write(marshalledJson) } -func (s *APIServer) runKelpCommandBlocking(namespace string, cmd string) ([]byte, error) { +func (s *APIServer) runKelpCommandBlocking(userID string, namespace string, cmd string) ([]byte, error) { // There is a weird issue on windows where the absolute path for the kelp binary does not work on the release GUI // version because of the unzipped directory name but it will work on the released cli version or if we change the // name of the folder in which the GUI version is unzipped. // To avoid these issues we only invoke with the binary name as opposed to the absolute path that contains the // directory name. see start_bot.go for some experimentation with absolute and relative paths cmdString := fmt.Sprintf("%s %s", s.kelpBinPath.Unix(), cmd) - return s.kos.Blocking(namespace, cmdString) + return s.kos.Blocking(userID, namespace, cmdString) } -func (s *APIServer) runKelpCommandBackground(namespace string, cmd string) (*kelpos.Process, error) { +func (s *APIServer) runKelpCommandBackground(userID string, namespace string, cmd string) (*kelpos.Process, error) { // There is a weird issue on windows where the absolute path for the kelp binary does not work on the release GUI // version because of the unzipped directory name but it will work on the released cli version or if we change the // name of the folder in which the GUI version is unzipped. // To avoid these issues we only invoke with the binary name as opposed to the absolute path that contains the // directory name. see start_bot.go for some experimentation with absolute and relative paths cmdString := fmt.Sprintf("%s %s", s.kelpBinPath.Unix(), cmd) - return s.kos.Background(namespace, cmdString) + return s.kos.Background(userID, namespace, cmdString) } func (s *APIServer) setupOpsDirectory(userID string) error { - e := s.kos.Mkdir(s.botConfigsPathForUser(userID)) + e := s.kos.Mkdir(userID, s.botConfigsPathForUser(userID)) if e != nil { return fmt.Errorf("error setting up configs directory (%s): %s", s.botConfigsPathForUser(userID).Native(), e) } - e = s.kos.Mkdir(s.botLogsPathForUser(userID)) + e = s.kos.Mkdir(userID, s.botLogsPathForUser(userID)) if e != nil { return fmt.Errorf("error setting up logs directory (%s): %s", s.botLogsPathForUser(userID).Native(), e) } diff --git a/gui/backend/delete_bot.go b/gui/backend/delete_bot.go index c010ad602..5b2f4bf85 100644 --- a/gui/backend/delete_bot.go +++ b/gui/backend/delete_bot.go @@ -90,7 +90,7 @@ func (s *APIServer) deleteBot(w http.ResponseWriter, r *http.Request) { // delete configs botPrefix := model2.GetPrefix(botName) botConfigPath := s.botConfigsPathForUser(req.UserData.ID).Join(botPrefix) - _, e = s.kos.Blocking("rm", fmt.Sprintf("rm %s*", botConfigPath.Unix())) + _, e = s.kos.Blocking(req.UserData.ID, "rm", fmt.Sprintf("rm %s*", botConfigPath.Unix())) if e != nil { s.writeKelpError(req.UserData, w, makeKelpErrorResponseWrapper( errorTypeBot, diff --git a/gui/backend/generate_bot_name.go b/gui/backend/generate_bot_name.go index 62618d3a7..02108bdf6 100644 --- a/gui/backend/generate_bot_name.go +++ b/gui/backend/generate_bot_name.go @@ -80,7 +80,7 @@ func (s *APIServer) doGenerateBotName(userData UserData) (string, error) { func (s *APIServer) prefixExists(userData UserData, prefix string) (bool, error) { command := fmt.Sprintf("ls %s | grep %s", s.botConfigsPathForUser(userData.ID).Unix(), prefix) - _, e := s.kos.Blocking("prefix", command) + _, e := s.kos.Blocking(userData.ID, "prefix", command) if e != nil { if strings.Contains(e.Error(), "exit status 1") { return false, nil diff --git a/gui/backend/list_bots.go b/gui/backend/list_bots.go index 6d8846ea8..7f7d5fa8d 100644 --- a/gui/backend/list_bots.go +++ b/gui/backend/list_bots.go @@ -53,7 +53,7 @@ func (s *APIServer) listBots(w http.ResponseWriter, r *http.Request) { func (s *APIServer) doListBots(userData UserData) ([]model2.Bot, error) { bots := []model2.Bot{} - resultBytes, e := s.kos.Blocking("ls", fmt.Sprintf("ls %s | sort", s.botConfigsPathForUser(userData.ID).Unix())) + resultBytes, e := s.kos.Blocking(userData.ID, "ls", fmt.Sprintf("ls %s | sort", s.botConfigsPathForUser(userData.ID).Unix())) if e != nil { return bots, fmt.Errorf("error when listing bots: %s", e) } diff --git a/gui/backend/start_bot.go b/gui/backend/start_bot.go index 3d9d63e71..89da09c76 100644 --- a/gui/backend/start_bot.go +++ b/gui/backend/start_bot.go @@ -146,7 +146,7 @@ func (s *APIServer) doStartBot(userData UserData, botName string, strategy strin } log.Printf("run command for bot '%s': %s\n", botName, command) - p, e := s.runKelpCommandBackground(botName, command) + p, e := s.runKelpCommandBackground(userData.ID, botName, command) if e != nil { return fmt.Errorf("could not start bot %s: %s", botName, e) } @@ -156,7 +156,7 @@ func (s *APIServer) doStartBot(userData UserData, botName string, strategy strin } go func(kelpCommand *exec.Cmd, name string) { - defer s.kos.SafeUnregister(name) + defer s.kos.SafeUnregister(userData.ID, name) e := kelpCommand.Wait() if e != nil { diff --git a/gui/backend/stop_bot.go b/gui/backend/stop_bot.go index 880b4b252..1d2ba559e 100644 --- a/gui/backend/stop_bot.go +++ b/gui/backend/stop_bot.go @@ -55,7 +55,7 @@ func (s *APIServer) doStopBot(userData UserData, botName string) error { return fmt.Errorf("error advancing bot state: %s", e) } - e = s.kos.Stop(botName) + e = s.kos.Stop(userData.ID, botName) if e != nil { return fmt.Errorf("error when killing bot %s: %s", botName, e) } diff --git a/gui/backend/version.go b/gui/backend/version.go index dd467177e..76aa684c6 100644 --- a/gui/backend/version.go +++ b/gui/backend/version.go @@ -6,19 +6,16 @@ import ( "strings" ) -func (s *APIServer) version(w http.ResponseWriter, r *http.Request) { - guiVersionBytes, e := s.runKelpCommandBlocking("version", "version | grep 'gui version' | cut -d':' -f2,3") - if e != nil { - s.writeError(w, fmt.Sprintf("error in version command: %s\n", e)) - return - } - cliVersionBytes, e := s.runKelpCommandBlocking("version", "version | grep 'cli version' | cut -d':' -f2,3") - if e != nil { - s.writeError(w, fmt.Sprintf("error in version command: %s\n", e)) - return - } +// this will be set automatically from root command +var versionString = "" + +// SetVersionString sets the version string to be displayed in the GUI +func SetVersionString(guiVersion string, cliVersion string) { + versionString = fmt.Sprintf("%s (%s)", strings.TrimSpace(guiVersion), strings.TrimSpace(cliVersion)) +} - versionBytes := []byte(fmt.Sprintf("%s (%s)", strings.TrimSpace(string(guiVersionBytes)), strings.TrimSpace(string(cliVersionBytes)))) +func (s *APIServer) version(w http.ResponseWriter, r *http.Request) { + versionBytes := []byte(versionString) w.WriteHeader(http.StatusOK) w.Write(versionBytes) } diff --git a/scripts/ccxt_bin_gen/ccxt_bin_gen.go b/scripts/ccxt_bin_gen/ccxt_bin_gen.go index f9d06607c..59a63beb8 100644 --- a/scripts/ccxt_bin_gen/ccxt_bin_gen.go +++ b/scripts/ccxt_bin_gen/ccxt_bin_gen.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/pkg/errors" + "github.com/stellar/kelp/support/kelpos" "github.com/stellar/kelp/support/networking" ) @@ -40,13 +41,14 @@ func main() { kos.SetSilentRegistrations() zipFoldername := fmt.Sprintf("ccxt-rest_%s-x64", goos) - generateCcxtBinary(kos, pkgos, zipFoldername) + // no need to pass a userID since we are not running under the context of any user at this point + generateCcxtBinary(kos, "_", pkgos, zipFoldername) } -func checkNodeVersion(kos *kelpos.KelpOS) { +func checkNodeVersion(kos *kelpos.KelpOS, userID string) { fmt.Printf("checking node version ... ") - version, e := kos.Blocking("node", "node -v") + version, e := kos.Blocking(userID, "node", "node -v") if e != nil { log.Fatal(errors.Wrap(e, "ensure that the `pkg` tool is installed correctly. You can get it from here https://github.com/zeit/pkg or by running `npm install -g pkg`")) } @@ -62,18 +64,18 @@ func checkNodeVersion(kos *kelpos.KelpOS) { fmt.Printf("valid\n") } -func checkPkgTool(kos *kelpos.KelpOS) { +func checkPkgTool(kos *kelpos.KelpOS, userID string) { fmt.Printf("checking for presence of `pkg` tool ... ") - _, e := kos.Blocking("pkg", "pkg -v") + _, e := kos.Blocking(userID, "pkg", "pkg -v") if e != nil { log.Fatal(errors.Wrap(e, "ensure that the `pkg` tool is installed correctly. You can get it from here https://github.com/zeit/pkg or by running `npm install -g pkg`")) } fmt.Printf("done\n") } -func downloadCcxtSource(kos *kelpos.KelpOS, downloadDir string) { +func downloadCcxtSource(kos *kelpos.KelpOS, userID string, downloadDir string) { fmt.Printf("making directory where we can download ccxt file %s ... ", downloadDir) - _, e := kos.Blocking("mkdir", fmt.Sprintf("mkdir -p %s", downloadDir)) + _, e := kos.Blocking(userID, "mkdir", fmt.Sprintf("mkdir -p %s", downloadDir)) if e != nil { log.Fatal(errors.Wrap(e, "could not make directory for downloadDir "+downloadDir)) } @@ -88,17 +90,17 @@ func downloadCcxtSource(kos *kelpos.KelpOS, downloadDir string) { fmt.Printf("done\n") fmt.Printf("untaring file %s ... ", downloadFilePath) - _, e = kos.Blocking("tar", fmt.Sprintf("tar xvf %s -C %s", downloadFilePath, downloadDir)) + _, e = kos.Blocking(userID, "tar", fmt.Sprintf("tar xvf %s -C %s", downloadFilePath, downloadDir)) if e != nil { log.Fatal(errors.Wrap(e, "could not untar ccxt file")) } fmt.Printf("done\n") } -func npmInstall(kos *kelpos.KelpOS, installDir string) { +func npmInstall(kos *kelpos.KelpOS, userID string, installDir string) { fmt.Printf("running npm install on directory %s ... ", installDir) npmCmd := fmt.Sprintf("cd %s && npm install && cd -", installDir) - _, e := kos.Blocking("npm", npmCmd) + _, e := kos.Blocking(userID, "npm", npmCmd) if e != nil { log.Fatal(errors.Wrap(e, "failed to run npm install")) } @@ -106,12 +108,12 @@ func npmInstall(kos *kelpos.KelpOS, installDir string) { } // pkg --targets node8-linux-x64 build/ccxt/ccxt-rest-0.0.4 -func runPkgTool(kos *kelpos.KelpOS, sourceDir string, outDir string, pkgos string) { +func runPkgTool(kos *kelpos.KelpOS, userID string, sourceDir string, outDir string, pkgos string) { target := fmt.Sprintf("node8-%s-x64", pkgos) fmt.Printf("running pkg tool on source directory %s with output directory as %s on target platform %s ... ", sourceDir, outDir, target) pkgCommand := fmt.Sprintf("pkg --out-path %s --targets %s %s", outDir, target, sourceDir) - outputBytes, e := kos.Blocking("pkg", pkgCommand) + outputBytes, e := kos.Blocking(userID, "pkg", pkgCommand) if e != nil { log.Fatal(errors.Wrap(e, "failed to run pkg tool")) } @@ -120,10 +122,10 @@ func runPkgTool(kos *kelpos.KelpOS, sourceDir string, outDir string, pkgos strin pkgCmdOutput := string(outputBytes) log.Printf("output of pkg script:\n%s", pkgCmdOutput) - copyDependencyFiles(kos, outDir, pkgCmdOutput) + copyDependencyFiles(kos, userID, outDir, pkgCmdOutput) } -func copyDependencyFiles(kos *kelpos.KelpOS, outDir string, pkgCmdOutput string) { +func copyDependencyFiles(kos *kelpos.KelpOS, userID string, outDir string, pkgCmdOutput string) { fmt.Println() fmt.Printf("copying dependency files to the output directory %s ...\n", outDir) for _, line := range strings.Split(pkgCmdOutput, "\n") { @@ -135,7 +137,7 @@ func copyDependencyFiles(kos *kelpos.KelpOS, outDir string, pkgCmdOutput string) fmt.Printf(" copying file %s to the output directory %s ... ", filename, outDir) cpCmd := fmt.Sprintf("cp %s %s", filename, outDir) - _, e := kos.Blocking("cp", cpCmd) + _, e := kos.Blocking(userID, "cp", cpCmd) if e != nil { log.Fatal(errors.Wrap(e, "failed to copy dependency file "+filename)) } @@ -145,20 +147,20 @@ func copyDependencyFiles(kos *kelpos.KelpOS, outDir string, pkgCmdOutput string) fmt.Println() } -func mkDir(kos *kelpos.KelpOS, zipDir string) { +func mkDir(kos *kelpos.KelpOS, userID string, zipDir string) { fmt.Printf("making directory %s ... ", zipDir) - _, e := kos.Blocking("mkdir", fmt.Sprintf("mkdir -p %s", zipDir)) + _, e := kos.Blocking(userID, "mkdir", fmt.Sprintf("mkdir -p %s", zipDir)) if e != nil { log.Fatal(errors.Wrap(e, "unable to make directory "+zipDir)) } fmt.Printf("done\n") } -func zipOutput(kos *kelpos.KelpOS, ccxtDir string, sourceDir string, zipFoldername string, zipOutDir string) { +func zipOutput(kos *kelpos.KelpOS, userID string, ccxtDir string, sourceDir string, zipFoldername string, zipOutDir string) { zipFilename := zipFoldername + ".zip" fmt.Printf("zipping directory %s as file %s ... ", filepath.Join(ccxtDir, ccxtBinOutputDir), zipFilename) zipCmd := fmt.Sprintf("cd %s && mv %s %s && zip -rq %s %s && cd - && mv %s %s", ccxtDir, ccxtBinOutputDir, zipFoldername, zipFilename, zipFoldername, filepath.Join(ccxtDir, zipFilename), zipOutDir) - _, e := kos.Blocking("zip", zipCmd) + _, e := kos.Blocking(userID, "zip", zipCmd) if e != nil { log.Fatal(errors.Wrap(e, "unable to zip folder with ccxt binary and dependencies")) } @@ -167,25 +169,25 @@ func zipOutput(kos *kelpos.KelpOS, ccxtDir string, sourceDir string, zipFolderna zipDirPath := filepath.Join(ccxtDir, zipFoldername) fmt.Printf("clean up zipped directory %s ... ", zipDirPath) cleanupCmd := fmt.Sprintf("rm %s/* && rmdir %s", zipDirPath, zipDirPath) - _, e = kos.Blocking("zip", cleanupCmd) + _, e = kos.Blocking(userID, "zip", cleanupCmd) if e != nil { log.Fatal(errors.Wrap(e, fmt.Sprintf("unable to cleanup zip folder %s with ccxt binary and dependencies", zipDirPath))) } fmt.Printf("done\n") } -func generateCcxtBinary(kos *kelpos.KelpOS, pkgos string, zipFoldername string) { - checkNodeVersion(kos) - checkPkgTool(kos) +func generateCcxtBinary(kos *kelpos.KelpOS, userID string, pkgos string, zipFoldername string) { + checkNodeVersion(kos, userID) + checkPkgTool(kos, userID) ccxtDir := filepath.Join(kelpPrefsDirectory, "ccxt") sourceDir := filepath.Join(ccxtDir, ccxtUntaredDirName) outDir := filepath.Join(ccxtDir, ccxtBinOutputDir) zipOutDir := filepath.Join(ccxtDir, "zipped") - downloadCcxtSource(kos, ccxtDir) - npmInstall(kos, sourceDir) - runPkgTool(kos, sourceDir, outDir, pkgos) - mkDir(kos, zipOutDir) - zipOutput(kos, ccxtDir, outDir, zipFoldername, zipOutDir) + downloadCcxtSource(kos, userID, ccxtDir) + npmInstall(kos, userID, sourceDir) + runPkgTool(kos, userID, sourceDir, outDir, pkgos) + mkDir(kos, userID, zipOutDir) + zipOutput(kos, userID, ccxtDir, outDir, zipFoldername, zipOutDir) } diff --git a/support/kelpos/process.go b/support/kelpos/process.go index 640c064b0..bb0175659 100644 --- a/support/kelpos/process.go +++ b/support/kelpos/process.go @@ -33,34 +33,34 @@ func (kos *KelpOS) StreamOutput(command *exec.Cmd) error { } // SafeUnregister ignores erros when unregistering the command at the provided namespace -func (kos *KelpOS) SafeUnregister(namespace string) { - kos.Unregister(namespace) +func (kos *KelpOS) SafeUnregister(userID string, namespace string) { + kos.Unregister(userID, namespace) } // Stop unregisters and stops the command at the provided namespace -func (kos *KelpOS) Stop(namespace string) error { - if p, exists := kos.GetProcess(namespace); exists { - e := kos.Unregister(namespace) +func (kos *KelpOS) Stop(userID string, namespace string) error { + if p, exists := kos.GetProcess(userID, namespace); exists { + e := kos.Unregister(userID, namespace) if e != nil { - return fmt.Errorf("could not stop command because of an error when unregistering command for namespace '%s': %s", namespace, e) + return fmt.Errorf("could not stop command because of an error when unregistering command for userID '%s' and namespace '%s': %s", userID, namespace, e) } log.Printf("killing process %d\n", p.Cmd.Process.Pid) return p.Cmd.Process.Kill() } - return fmt.Errorf("process with namespace does not exist: %s", namespace) + return fmt.Errorf("process with userID '%s' and namespace '%s' does not exist", userID, namespace) } // Blocking runs a bash command and blocks -func (kos *KelpOS) Blocking(namespace string, cmd string) ([]byte, error) { - p, e := kos.Background(namespace, cmd) +func (kos *KelpOS) Blocking(userID string, namespace string, cmd string) ([]byte, error) { + p, e := kos.Background(userID, namespace, cmd) if e != nil { return nil, fmt.Errorf("could not run bash command in background '%s': %s", cmd, e) } // defer unregistration of process because regardless of whether it succeeds or fails it will not be active on the system anymore defer func() { - eInner := kos.Unregister(namespace) + eInner := kos.Unregister(userID, namespace) if eInner != nil { log.Fatalf("error unregistering bash command '%s': %s", cmd, eInner) } @@ -86,15 +86,15 @@ func (kos *KelpOS) Blocking(namespace string, cmd string) ([]byte, error) { // now check for errors if eWait != nil || eRead != nil { - return nil, fmt.Errorf("error in bash command '%s' for namespace '%s': (eWait=%s, outputBytes=%s, eRead=%v)", - cmd, namespace, eWait, string(outputBytes), eRead) + return nil, fmt.Errorf("error in bash command '%s' for userID '%s' and namespace '%s': (eWait=%s, outputBytes=%s, eRead=%v)", + cmd, userID, namespace, eWait, string(outputBytes), eRead) } return outputBytes, nil } // Background runs the provided bash command in the background and registers the command -func (kos *KelpOS) Background(namespace string, cmd string) (*Process, error) { +func (kos *KelpOS) Background(userID string, namespace string, cmd string) (*Process, error) { c := exec.Command("bash", "-c", cmd) // always execute commands from the working directory (specify as native since underlying OS handles it) // using dotKelpWorkingDir as working directory since all our config files and log files are located in here and we want @@ -121,7 +121,7 @@ func (kos *KelpOS) Background(namespace string, cmd string) (*Process, error) { Stdin: stdinWriter, Stdout: stdoutReader, } - e = kos.register(namespace, p) + e = kos.register(userID, namespace, p) if e != nil { return nil, fmt.Errorf("error registering bash command '%s': %s", cmd, e) } @@ -129,59 +129,62 @@ func (kos *KelpOS) Background(namespace string, cmd string) (*Process, error) { return p, nil } -func (kos *KelpOS) register(namespace string, p *Process) error { +func (kos *KelpOS) register(userID string, namespace string, p *Process) error { kos.processLock.Lock() defer kos.processLock.Unlock() - if _, exists := kos.processes[namespace]; exists { - return fmt.Errorf("process with namespace already exists: %s", namespace) + key := fmt.Sprintf("%s:%s", userID, namespace) + if _, exists := kos.processes[key]; exists { + return fmt.Errorf("process with key already exists: %s", key) } - kos.processes[namespace] = *p + kos.processes[key] = *p if !kos.silentRegistrations { - log.Printf("registered command under namespace '%s' with PID: %d, processes available: %v\n", namespace, p.Cmd.Process.Pid, kos.RegisteredProcesses()) + log.Printf("registered command under key '%s' with PID: %d, processes available: %v\n", key, p.Cmd.Process.Pid, kos.RegisteredProcesses()) } return nil } // Unregister unregisters the command at the provided namespace, returning an error if needed -func (kos *KelpOS) Unregister(namespace string) error { +func (kos *KelpOS) Unregister(userID string, namespace string) error { kos.processLock.Lock() defer kos.processLock.Unlock() - if p, exists := kos.processes[namespace]; exists { - delete(kos.processes, namespace) + key := fmt.Sprintf("%s:%s", userID, namespace) + if p, exists := kos.processes[key]; exists { + delete(kos.processes, key) if !kos.silentRegistrations { - log.Printf("unregistered command under namespace '%s' with PID: %d, processes available: %v\n", namespace, p.Cmd.Process.Pid, kos.RegisteredProcesses()) + log.Printf("unregistered command under key '%s' with PID: %d, processes available: %v\n", key, p.Cmd.Process.Pid, kos.RegisteredProcesses()) } return nil } - return fmt.Errorf("process with namespace does not exist: %s", namespace) + return fmt.Errorf("process with key does not exist: %s", key) } // GetProcess gets the process tied to the provided namespace -func (kos *KelpOS) GetProcess(namespace string) (*Process, bool) { +func (kos *KelpOS) GetProcess(userID string, namespace string) (*Process, bool) { kos.processLock.Lock() defer kos.processLock.Unlock() - p, exists := kos.processes[namespace] + key := fmt.Sprintf("%s:%s", userID, namespace) + p, exists := kos.processes[key] return &p, exists } // RegisteredProcesses returns the list of registered processes func (kos *KelpOS) RegisteredProcesses() []string { list := []string{} - for k, _ := range kos.processes { + for k := range kos.processes { list = append(list, k) } return list } // Mkdir function with a neat error message -func (kos *KelpOS) Mkdir(dirPath *OSPath) error { - _, e := kos.Blocking("mkdir", fmt.Sprintf("mkdir -p %s", dirPath.Unix())) +func (kos *KelpOS) Mkdir(userID string, dirPath *OSPath) error { + _, e := kos.Blocking(userID, "mkdir", fmt.Sprintf("mkdir -p %s", dirPath.Unix())) if e != nil { - return fmt.Errorf("error running mkdir command for dir (%s): %s\n", dirPath.AsString(), e) + return fmt.Errorf("error running mkdir command for dir (%s): %s", dirPath.AsString(), e) } return nil } diff --git a/support/kelpos/userBotData.go b/support/kelpos/userBotData.go index c47d7b27c..0beda80e4 100644 --- a/support/kelpos/userBotData.go +++ b/support/kelpos/userBotData.go @@ -129,7 +129,7 @@ func (ubd *UserBotData) QueryBotState(botName string) (BotState, error) { prefix := getBotNamePrefix(botName) command := fmt.Sprintf("ps aux | grep trade | grep %s | grep -v grep", prefix) - outputBytes, e := ubd.kos.Blocking(fmt.Sprintf("query_bot_state: %s", botName), command) + outputBytes, e := ubd.kos.Blocking(ubd.user.ID, fmt.Sprintf("query_bot_state: %s", botName), command) if e != nil { if strings.Contains(e.Error(), "exit status 1") { return BotStateStopped, nil