Skip to content
This repository was archived by the owner on Feb 1, 2024. It is now read-only.

Commit 8002a95

Browse files
committed
Kelp GUI: make process namespace multi-tenant and thread through
bots using the same name from different users (such as autogen bot) could not be started simultaneously. now we have pre-pended the user_id to the process map key
1 parent 86214e8 commit 8002a95

12 files changed

Lines changed: 109 additions & 95 deletions

File tree

cmd/root.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/spf13/cobra"
1111

12+
"github.com/stellar/kelp/gui/backend"
1213
"github.com/stellar/kelp/support/networking"
1314
"github.com/stellar/kelp/support/sdk"
1415
"github.com/stellar/kelp/support/utils"
@@ -71,6 +72,7 @@ var rootCcxtRestURL *string
7172

7273
func init() {
7374
validateBuild()
75+
backend.SetVersionString(guiVersion, version)
7476

7577
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)")
7678

cmd/server_amd64.go

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ func init() {
126126

127127
uiLogsDirPath := kos.GetDotKelpWorkingDir().Join(uiLogsDir)
128128
log.Printf("calling mkdir on uiLogsDirPath: %s ...", uiLogsDirPath.AsString())
129-
e = kos.Mkdir(uiLogsDirPath)
129+
// no need to pass a userID since we are not running under the context of any user at this point
130+
e = kos.Mkdir("_", uiLogsDirPath)
130131
if e != nil {
131132
panic(errors.Wrap(e, "could not mkdir on uiLogsDirPath: "+uiLogsDirPath.AsString()))
132133
}
@@ -293,29 +294,34 @@ func init() {
293294
ccxtBinPath := ccxtDestDir.Join(ccxtBinaryName)
294295

295296
log.Printf("mkdir ccxtDirPath: %s ...", ccxtDirPath.AsString())
296-
e := kos.Mkdir(ccxtDirPath)
297+
// no need to pass a userID since we are not running under the context of any user at this point
298+
e := kos.Mkdir("_", ccxtDirPath)
297299
if e != nil {
298300
panic(fmt.Errorf("could not mkdir for ccxtDirPath: %s", e))
299301
}
300302

301303
if runtime.GOOS == "windows" {
302304
ccxtSourceDir := kos.GetBinDir().Join("ccxt").Join(ccxtFilenameNoExt)
303-
e = copyCcxtFolder(kos, ccxtSourceDir, ccxtDestDir)
305+
// no need to pass a userID since we are not running under the context of any user at this point
306+
e = copyCcxtFolder(kos, "_", ccxtSourceDir, ccxtDestDir)
304307
if e != nil {
305308
panic(e)
306309
}
307310
} else {
308311
ccxtBundledZipPath := kos.GetBinDir().Join("ccxt").Join(filenameWithExt)
309312
ccxtZipDestPath := ccxtDirPath.Join(filenameWithExt)
310-
e = copyOrDownloadCcxtBinary(kos, ccxtBundledZipPath, ccxtZipDestPath, filenameWithExt)
313+
// no need to pass a userID since we are not running under the context of any user at this point
314+
e = copyOrDownloadCcxtBinary(kos, "_", ccxtBundledZipPath, ccxtZipDestPath, filenameWithExt)
311315
if e != nil {
312316
panic(e)
313317
}
314318

315-
unzipCcxtFile(kos, ccxtDirPath, ccxtBinPath, filenameWithExt)
319+
// no need to pass a userID since we are not running under the context of any user at this point
320+
unzipCcxtFile(kos, "_", ccxtDirPath, ccxtBinPath, filenameWithExt)
316321
}
317322

318-
e = runCcxtBinary(kos, ccxtBinPath)
323+
// no need to pass a userID since we are not running under the context of any user at this point
324+
e = runCcxtBinary(kos, "_", ccxtBinPath)
319325
if e != nil {
320326
panic(e)
321327
}
@@ -476,13 +482,14 @@ func setMiddleware(r *chi.Mux) {
476482

477483
func copyCcxtFolder(
478484
kos *kelpos.KelpOS,
485+
userID string,
479486
ccxtSourceDir *kelpos.OSPath,
480487
ccxtDestDir *kelpos.OSPath,
481488
) error {
482489
log.Printf("copying ccxt directory from %s to location %s ...", ccxtSourceDir.AsString(), ccxtDestDir.AsString())
483490

484491
cpCmd := fmt.Sprintf("cp -a %s %s", ccxtSourceDir.Unix(), ccxtDestDir.Unix())
485-
_, e := kos.Blocking("cp-ccxt", cpCmd)
492+
_, e := kos.Blocking(userID, "cp-ccxt", cpCmd)
486493
if e != nil {
487494
return fmt.Errorf("unable to copy ccxt directory from %s to %s: %s", ccxtSourceDir.AsString(), ccxtDestDir.AsString(), e)
488495
}
@@ -493,6 +500,7 @@ func copyCcxtFolder(
493500

494501
func copyOrDownloadCcxtBinary(
495502
kos *kelpos.KelpOS,
503+
userID string,
496504
ccxtBundledZipPath *kelpos.OSPath,
497505
ccxtZipDestPath *kelpos.OSPath,
498506
filenameWithExt string,
@@ -505,7 +513,7 @@ func copyOrDownloadCcxtBinary(
505513
log.Printf("copying ccxt from %s to location %s ...", ccxtBundledZipPath.Unix(), ccxtZipDestPath.Unix())
506514

507515
cpCmd := fmt.Sprintf("cp %s %s", ccxtBundledZipPath.Unix(), ccxtZipDestPath.Unix())
508-
_, e = kos.Blocking("cp-ccxt", cpCmd)
516+
_, e = kos.Blocking(userID, "cp-ccxt", cpCmd)
509517
if e != nil {
510518
return fmt.Errorf("unable to copy ccxt zip file from %s to %s: %s", ccxtBundledZipPath.Unix(), ccxtZipDestPath.Unix(), e)
511519
}
@@ -546,6 +554,7 @@ func copyOrDownloadCcxtBinary(
546554

547555
func unzipCcxtFile(
548556
kos *kelpos.KelpOS,
557+
userID string,
549558
ccxtDir *kelpos.OSPath,
550559
ccxtBinPath *kelpos.OSPath,
551560
filenameWithExt string,
@@ -558,21 +567,21 @@ func unzipCcxtFile(
558567

559568
log.Printf("unzipping file %s ... ", filenameWithExt)
560569
zipCmd := fmt.Sprintf("cd %s && unzip %s", ccxtDir.Unix(), filenameWithExt)
561-
_, e := kos.Blocking("zip", zipCmd)
570+
_, e := kos.Blocking(userID, "zip", zipCmd)
562571
if e != nil {
563572
log.Fatal(errors.Wrap(e, fmt.Sprintf("unable to unzip file %s in directory %s", filenameWithExt, ccxtDir.AsString())))
564573
}
565574
log.Printf("done\n")
566575
}
567576

568-
func runCcxtBinary(kos *kelpos.KelpOS, ccxtBinPath *kelpos.OSPath) error {
577+
func runCcxtBinary(kos *kelpos.KelpOS, userID string, ccxtBinPath *kelpos.OSPath) error {
569578
if _, e := os.Stat(ccxtBinPath.Native()); os.IsNotExist(e) {
570579
return fmt.Errorf("path to ccxt binary (%s) does not exist", ccxtBinPath.AsString())
571580
}
572581

573582
log.Printf("running binary %s", ccxtBinPath.AsString())
574583
// 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
575-
_, e := kos.Background("ccxt-rest", ccxtBinPath.Unix())
584+
_, e := kos.Background(userID, "ccxt-rest", ccxtBinPath.Unix())
576585
if e != nil {
577586
log.Fatal(errors.Wrap(e, fmt.Sprintf("unable to run ccxt file at location %s", ccxtBinPath.AsString())))
578587
}
@@ -653,7 +662,8 @@ func writeTrayIcon(kos *kelpos.KelpOS, trayIconPath *kelpos.OSPath, assetsDirPat
653662
// create dir if not exists
654663
if _, e := os.Stat(assetsDirPath.Native()); os.IsNotExist(e) {
655664
log.Printf("mkdir assetsDirPath: %s ...", assetsDirPath.AsString())
656-
e = kos.Mkdir(assetsDirPath)
665+
// no need to pass a userID since we are not running under the context of any user at this point
666+
e = kos.Mkdir("_", assetsDirPath)
657667
if e != nil {
658668
return errors.Wrap(e, "could not mkdir for assetsDirPath: "+assetsDirPath.AsString())
659669
}

gui/backend/api_server.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -281,33 +281,33 @@ func (s *APIServer) writeJsonWithLog(w http.ResponseWriter, v interface{}, doLog
281281
w.Write(marshalledJson)
282282
}
283283

284-
func (s *APIServer) runKelpCommandBlocking(namespace string, cmd string) ([]byte, error) {
284+
func (s *APIServer) runKelpCommandBlocking(userID string, namespace string, cmd string) ([]byte, error) {
285285
// There is a weird issue on windows where the absolute path for the kelp binary does not work on the release GUI
286286
// version because of the unzipped directory name but it will work on the released cli version or if we change the
287287
// name of the folder in which the GUI version is unzipped.
288288
// To avoid these issues we only invoke with the binary name as opposed to the absolute path that contains the
289289
// directory name. see start_bot.go for some experimentation with absolute and relative paths
290290
cmdString := fmt.Sprintf("%s %s", s.kelpBinPath.Unix(), cmd)
291-
return s.kos.Blocking(namespace, cmdString)
291+
return s.kos.Blocking(userID, namespace, cmdString)
292292
}
293293

294-
func (s *APIServer) runKelpCommandBackground(namespace string, cmd string) (*kelpos.Process, error) {
294+
func (s *APIServer) runKelpCommandBackground(userID string, namespace string, cmd string) (*kelpos.Process, error) {
295295
// There is a weird issue on windows where the absolute path for the kelp binary does not work on the release GUI
296296
// version because of the unzipped directory name but it will work on the released cli version or if we change the
297297
// name of the folder in which the GUI version is unzipped.
298298
// To avoid these issues we only invoke with the binary name as opposed to the absolute path that contains the
299299
// directory name. see start_bot.go for some experimentation with absolute and relative paths
300300
cmdString := fmt.Sprintf("%s %s", s.kelpBinPath.Unix(), cmd)
301-
return s.kos.Background(namespace, cmdString)
301+
return s.kos.Background(userID, namespace, cmdString)
302302
}
303303

304304
func (s *APIServer) setupOpsDirectory(userID string) error {
305-
e := s.kos.Mkdir(s.botConfigsPathForUser(userID))
305+
e := s.kos.Mkdir(userID, s.botConfigsPathForUser(userID))
306306
if e != nil {
307307
return fmt.Errorf("error setting up configs directory (%s): %s", s.botConfigsPathForUser(userID).Native(), e)
308308
}
309309

310-
e = s.kos.Mkdir(s.botLogsPathForUser(userID))
310+
e = s.kos.Mkdir(userID, s.botLogsPathForUser(userID))
311311
if e != nil {
312312
return fmt.Errorf("error setting up logs directory (%s): %s", s.botLogsPathForUser(userID).Native(), e)
313313
}

gui/backend/delete_bot.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ func (s *APIServer) deleteBot(w http.ResponseWriter, r *http.Request) {
9090
// delete configs
9191
botPrefix := model2.GetPrefix(botName)
9292
botConfigPath := s.botConfigsPathForUser(req.UserData.ID).Join(botPrefix)
93-
_, e = s.kos.Blocking("rm", fmt.Sprintf("rm %s*", botConfigPath.Unix()))
93+
_, e = s.kos.Blocking(req.UserData.ID, "rm", fmt.Sprintf("rm %s*", botConfigPath.Unix()))
9494
if e != nil {
9595
s.writeKelpError(req.UserData, w, makeKelpErrorResponseWrapper(
9696
errorTypeBot,

gui/backend/generate_bot_name.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func (s *APIServer) doGenerateBotName(userData UserData) (string, error) {
8080

8181
func (s *APIServer) prefixExists(userData UserData, prefix string) (bool, error) {
8282
command := fmt.Sprintf("ls %s | grep %s", s.botConfigsPathForUser(userData.ID).Unix(), prefix)
83-
_, e := s.kos.Blocking("prefix", command)
83+
_, e := s.kos.Blocking(userData.ID, "prefix", command)
8484
if e != nil {
8585
if strings.Contains(e.Error(), "exit status 1") {
8686
return false, nil

gui/backend/list_bots.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func (s *APIServer) listBots(w http.ResponseWriter, r *http.Request) {
5353

5454
func (s *APIServer) doListBots(userData UserData) ([]model2.Bot, error) {
5555
bots := []model2.Bot{}
56-
resultBytes, e := s.kos.Blocking("ls", fmt.Sprintf("ls %s | sort", s.botConfigsPathForUser(userData.ID).Unix()))
56+
resultBytes, e := s.kos.Blocking(userData.ID, "ls", fmt.Sprintf("ls %s | sort", s.botConfigsPathForUser(userData.ID).Unix()))
5757
if e != nil {
5858
return bots, fmt.Errorf("error when listing bots: %s", e)
5959
}

gui/backend/start_bot.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ func (s *APIServer) doStartBot(userData UserData, botName string, strategy strin
146146
}
147147
log.Printf("run command for bot '%s': %s\n", botName, command)
148148

149-
p, e := s.runKelpCommandBackground(botName, command)
149+
p, e := s.runKelpCommandBackground(userData.ID, botName, command)
150150
if e != nil {
151151
return fmt.Errorf("could not start bot %s: %s", botName, e)
152152
}
@@ -156,7 +156,7 @@ func (s *APIServer) doStartBot(userData UserData, botName string, strategy strin
156156
}
157157

158158
go func(kelpCommand *exec.Cmd, name string) {
159-
defer s.kos.SafeUnregister(name)
159+
defer s.kos.SafeUnregister(userData.ID, name)
160160

161161
e := kelpCommand.Wait()
162162
if e != nil {

gui/backend/stop_bot.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func (s *APIServer) doStopBot(userData UserData, botName string) error {
5555
return fmt.Errorf("error advancing bot state: %s", e)
5656
}
5757

58-
e = s.kos.Stop(botName)
58+
e = s.kos.Stop(userData.ID, botName)
5959
if e != nil {
6060
return fmt.Errorf("error when killing bot %s: %s", botName, e)
6161
}

gui/backend/version.go

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,16 @@ import (
66
"strings"
77
)
88

9-
func (s *APIServer) version(w http.ResponseWriter, r *http.Request) {
10-
guiVersionBytes, e := s.runKelpCommandBlocking("version", "version | grep 'gui version' | cut -d':' -f2,3")
11-
if e != nil {
12-
s.writeError(w, fmt.Sprintf("error in version command: %s\n", e))
13-
return
14-
}
15-
cliVersionBytes, e := s.runKelpCommandBlocking("version", "version | grep 'cli version' | cut -d':' -f2,3")
16-
if e != nil {
17-
s.writeError(w, fmt.Sprintf("error in version command: %s\n", e))
18-
return
19-
}
9+
// this will be set automatically from root command
10+
var versionString = ""
11+
12+
// SetVersionString sets the version string to be displayed in the GUI
13+
func SetVersionString(guiVersion string, cliVersion string) {
14+
versionString = fmt.Sprintf("%s (%s)", strings.TrimSpace(guiVersion), strings.TrimSpace(cliVersion))
15+
}
2016

21-
versionBytes := []byte(fmt.Sprintf("%s (%s)", strings.TrimSpace(string(guiVersionBytes)), strings.TrimSpace(string(cliVersionBytes))))
17+
func (s *APIServer) version(w http.ResponseWriter, r *http.Request) {
18+
versionBytes := []byte(versionString)
2219
w.WriteHeader(http.StatusOK)
2320
w.Write(versionBytes)
2421
}

0 commit comments

Comments
 (0)